OpenAI Compatible Chat Provider

Base class for prompt providers.

class smarter.apps.provider.services.text_completion.lib.openai_compatible_chat_provider.OpenAISmarterClient(provider, provider_name, base_url, api_key, default_model, default_system_role='You are a helpful llm_client. When given the opportunity to utilize function calling, you should always do so. This will allow you to provide the best possible responses to the user. If you are unable to provide a response, you should prompt the user for more information. If you are still unable to provide a response, you should inform the user that you are unable to help them at this time.', default_temperature=0.5, default_max_tokens=2048, valid_chat_completion_models=None, add_built_in_tools=False, **kwargs)[source]

Bases: SmarterChatProviderBase

Prompt provider for OpenAI-compatible text completion APIs.

This provider class enables seamless integration with any vendor or service that implements the OpenAI prompt completion API, including both OpenAI and third-party providers that adhere to the same protocol and message formats.

Key Features:

  • Supports OpenAI’s prompt completion API and compatible alternatives.

  • Handles message formatting, tool calls, plugin integration, and billing.

  • Manages multi-step prompt completion workflows, including tool and plugin responses.

  • Provides hooks for plugin selection, function registration, and error handling.

Usage:

Inherit from this class to implement a prompt provider that communicates with any OpenAI-compatible API endpoint. This class is suitable for use cases where you want to support multiple LLM vendors with a unified interface.

Example:

class MyProvider(OpenAISmarterClient):
    pass

provider = MyProvider()
response = provider.handler(user_profile, prompt, data)
append_openai_error_response(response, e=None)[source]

Append an error message to the internal message list based on the OpenAI response.

Parameters:

response (ChatCompletion) – The OpenAI prompt completion response containing the error.

Returns:

None

Return type:

None

append_openai_response(response)[source]

Append the OpenAI-compatible response message to the internal message list.

2025-06-20: updated to use model_dump_json() to ensure compatibility with Pydantic v2. 2025-10-02: updated to validate that the response message is indeed a ChatCompletionMessage.

Parameters:

response (ChatCompletion) – The OpenAI-compatible prompt completion response.

Returns:

None

Return type:

None

available_functions: dict[str, Any]
completion_tokens: int | None
data: dict[str, Any] | None
first_iteration: dict[str, Any]
first_response: ChatCompletion | None
functions: List[str] | None
handle_completion()[source]

Handle prompt completion response.

This method formats the final response to be returned to the client.

Returns:

A dictionary representing the final prompt completion response.

Return type:

dict

handle_function_provided(function)[source]

Handle a function being provided.

Parameters:

function (str) – The name of the function that was provided.

Returns:

None

Return type:

None

handle_plugin_called(plugin)[source]

Handle a plugin tool call.

example: SqlPlugin, ApiPlugin, StaticPlugin etc.

Parameters:

plugin (PluginBase) – The plugin instance that was called.

Returns:

None

Return type:

None

handle_plugin_selected(plugin)[source]

Handle a plugin being selected.

does the prompt have anything to do with any of the search terms defined in a plugin? TODO: need to decide on how to resolve which of many plugin values sets to use for model, temperature, max_completion_tokens 2025-10-02: updated to validate that messages and tools are lists. 2025-10-02: updated to use plugin.plugin_meta.name for the plugin name.

Parameters:

plugin (PluginBase) – The plugin instance that was selected.

Returns:

None

Return type:

None

handle_response()[source]

Handle internal billing, and append messages to the response for prompt completion and the billing summary.

Returns:

None

Return type:

None

handle_tool_called(function_name, function_args)[source]

Handle a built-in tool call.

example: get_current_weather()

Parameters:
  • function_name (str) – The name of the tool function called.

  • function_args (str) – The arguments passed to the tool function.

Returns:

None

Return type:

None

handler(user_profile, prompt, data, plugins=None, functions=None)[source]

Process a prompt prompt request and invoke the appropriate OpenAI-compatible API endpoint.

This method orchestrates the entire prompt completion workflow, including:

  • Validating input and internal state.

  • Initializing or updating the message thread.

  • Selecting and configuring plugins and/or functions (collectively, tool calls) for the LLM.

  • Preparing and sending requests to the OpenAI API (or compatible provider).

  • Handling tool calls and plugin responses.

  • Managing billing, logging, and signal dispatch.

  • Returning a formatted HTTP response with the LLM’s output and relevant metadata.

Parameters:
  • user_profile (UserProfile) – The user_profile instance making the request.

  • prompt (Prompt) – The prompt session instance associated with this request.

  • data – The request payload, typically containing a session key and a list of message dictionaries.

  • plugins (Optional[list[PluginBase]]) – A list of plugin instances to be considered for selection and presentation to the LLM.

  • functions (Optional[list[str]]) – A list of predefined function definitions for tool calls.

Returns:

An HTTP response dictionary (or list) containing the LLM’s output, tool call results, and metadata.

Return type:

Union[dict[str, Any], SmarterHttpResponseForbidden, SmarterHttpResponseNotFound, SmarterHttpResponseBadRequest, SmarterJournaledJsonErrorResponse, SmarterJournaledJsonResponse]

Raises:
  • SmarterValueError – If required parameters are missing or invalid.

  • SmarterConfigurationError – If there are configuration issues with the provider or plugins.

  • SmarterIlligalInvocationError – If the method is invoked in an invalid state.

Note

This method manages both the initial and any required follow-up LLM requests (e.g., for tool calls). It also handles plugin selection logic and ensures that all required signals and billing events are triggered.

See also

_InternalKeys OpenAIMessageKeys PluginBase ChatCompletion ChatCompletionMessageToolCall

Example usage:

response = provider.handler(
    prompt=chat_instance,
    data=request_data,
    plugins=[plugin1, plugin2],
    functions=[function_definition_1, function_definition_2],
    user_profile=current_user
)
input_text: str | None
iteration: int
max_completion_tokens: int | None
model: str | None
property new_messages: list[dict[str, Any]]

Return a list of messages that are marked as new.

This property filters the internal message list to return only those messages that have the ‘smarter_is_new’ flag set to True.

Returns:

A list of new messages.

Return type:

list[dict[str, Any]]

property openai_messages: list[dict[str, Any]]

Return a sanitized list of messages compatible with OpenAI’s prompt completion API.

This property processes the internal message list, removing Smarter-specific annotations (such as metadata about tool calls and interim completion token charges) to ensure that only valid OpenAI message fields are included. This is essential for avoiding API errors related to unexpected or extraneous fields.

Returns:

A list of dictionaries representing prompt messages, formatted for OpenAI’s API.

Return type:

list[dict[str, Any]]

Raises:

SmarterValueError – If the internal message list is not a list.

Example:

[
    {
        "role": "assistant",
        "content": "Welcome to Smarter!",
        "tool_calls": [
            {
                "id": "call_ABC123",
                "type": "function",
                "function": {
                    "name": "smarter_plugin_0000000045",
                    "arguments": "{"description":"AI"}"
                }
            }
        ]
    },
    {
        "role": "tool",
        "name": "smarter_plugin_0000000045",
        "content": "SqlPlugin stackademy_sql response: ...",
        "tool_call_id": "call_ABC123"
    }
]

Important

  • OpenAI expects that every assistant message with a tool_calls field is immediately followed by a corresponding tool message for each tool_call_id. Failure to do so will result in an API error.

  • If you include Smarter-specific fields (such as smarter_is_new) in the message list, OpenAI’s API may reject the request.

  • On the first iteration, tool call responses are excluded from the message list to comply with OpenAI’s requirements.

plugins: List[PluginBase] | None
prep_first_request()[source]

Prepare the first request for the prompt completion.

This is called at the beginning of the prompt completion process.

Raises:

SmarterValueError – If the messages are not a list, or if tool definitions are invalid.

Returns:

None

Return type:

None

prep_second_request()[source]

Prepare the second request for the prompt completion.

This is called in response to a tool call that requires a second request to the LLM.

Returns:

None

Return type:

None

process_tool_call(tool_call)[source]

Process a tool call from the LLM.

This method handles both built-in tool calls and plugin tool calls.

Parameters:

tool_call (Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall]) – The tool call data from the LLM.

Returns:

None

Return type:

None

prompt_tokens: int | None
reference: str | None
request_meta_data: dict[str, Any]
request_meta_data_factory()[source]

Return a dictionary of request meta data.

This includes the model, temperature, max_completion_tokens, and input_text.

Returns:

A dictionary of request meta data.

Return type:

dict

second_iteration: dict[str, Any] | None
second_response: ChatCompletion | None
serialized_tool_calls: list[dict[str, Any]] | None
temperature: float | None
tools: list[dict[str, Any]] | None
total_tokens: int | None
smarter.apps.provider.services.text_completion.lib.openai_compatible_chat_provider.should_log(level)[source]

Check if logging should be done based on the waffle switch.