Base AI Model¶
For developers only
This page is addressed to developers who want to extend the BaseAIModel
class.
End users should use classes such as OpenAIModel
.
More information in the 'How-to' section
We also have a How-to section on adding a new AI provider , which provides a step-by-step guide on how to add a new AI provider to Conatus.
The BaseAIModel class is an abstract
class that defines the interface for all AI models. It is meant to be
a wrapper around an AI model's client and its call function.
Note
Here, "call function" means the method, generally provided by the AI provider's SDK, that generates a response from a prompt. The actual name of that function might vary from provider to provider ("create", "generate", etc.)
One instantiation per model configuration¶
There should be one instantiation of the class per model configuration. In multi-agent scenarios, you might have multiple instantiations of the class, but each one should be for a different model configuration.
Therefore, you should design your classes so that instantiating a new model with a given configuration is really quick. For example, it's probably a bad idea to create a new client for every instance of the class, since it generally entails some network calls.
We provide the convenience method with_config
to help you create a new
instance of the class with a given configuration, so that you don't have to
set up the client again. Once again, this should be useful in multi-agent
system.
Initialization¶
The __init__ method is already implemented, and offers a few features:
- It calls
ModelConfig.apply_config, which automatically reconciles the configuration passed by the user with the default configuration (see below), which is obtained by calling thedefault_configmethod.
- It initializes the client, either by using the client passed by the user,
or by calling the
default_clientmethod. That method, which needs to be implemented by subclasses, is responsible for creating the client. If your subclass implements theapi_key_env_variableclass attribute, the default__init__method will use theget_api_keymethod to retrieve the API key from the environment variables.
Configuration¶
We expect the config attribute of the class to be of type ModelConfig
.
Creating a custom configuration¶
You should create a subclass of ModelConfig
to support your specific needs (e.g. adding new arguments if your AI
provider supports specific arguments in their API.) This is one of the
reasons why BaseAIModel is a generic
class; this way, you can be sure that if you add configuration arguments,
you can retrieve them within the class without issues.
For instance, OpenAIModelConfig
has a timeout argument,
which is not present in ModelConfig.
Configuring an AI model from a user perspective¶
Here's how the user should handle configuration values:
- The configuration might be passed either at
__init__or at thecallmethod. In either case, you must try to honor its values. If they differ, the values passed at thecallmethod will override the ones passed at the__init__method.
- Users should be able to pass a dictionary to the
callmethod, and the values will be used to override the ones in the configuration. In fact, for thecallmethod, the user should generally pass a dictionary. If they don't do this, any keyword argument that is not explicitly passed will be reset to a default value, which might not be the intended effect.
We provide a convenience method ModelConfig.apply_config
to help you reconcile
the default configuration with the user-provided configuration.
Passing the configuration to the AI model¶
One thing you want to do with ModelConfig
is to pass the values directly
to the AI model through its calling method. For this, you can use the
to_kwargs method, which
returns a dictionary with the configuration, and can be then passed
as keyword arguments in your call method.
Nevertheless, some attributes in ModelConfig
are not meant to be passed in the call
method. For instance:
- The
api_keyattribute is not meant to be passed in the call method, because it's meant to be set in the client.
- The
stdout_modeattribute is not meant to be passed in the call method, because it's meant to be set in the printing mix-in.
In order to distinguish between the two, you can define a TypedDict
that matches the arguments expected by the provider
in the call method, and then use it as the specification in the
to_kwargs method.
One example of this is OpenAIModelCCSpec
, which ensures that only
the arguments expected by the OpenAI API are passed in the call method.
Mocks¶
You can set the use_mock attribute to True to use a mock client. We
try always to set it to True during testing. One mechanism to do this is
to set the ALWAYS_USE_MOCK environment variable to True. You should
try to honor this.
The call methods¶
Any subclass needs to implement four methods that wrap around the underlying call function of the AI model. These are:
call, for synchronous callsacall, for asynchronous callscall_stream, for synchronous streaming callsacall_stream, for asynchronous streaming calls
All of these functions need to return an AIResponse
object.
The simple_call method is
a convenience method for the call
method.
Converting messages and tools to the AI model format¶
The BaseAIModel class provides two
methods to convert the messages and tools to the AI model format:
These methods are meant to be overridden by subclasses.
In turn, these methods are leveraged by the prepare_call_args
method. This method
can be used as is, as long as you override the methods above.
conatus.models.base.BaseAIModel
¶
BaseAIModel(
model_config: ModelConfig | ModelConfigTD | None = None,
client: ClientType = None,
*,
get_default_client_if_not_given: bool = True,
api_key: str | None = None,
model_name: str | None = None,
model_type: ModelType | None = None,
**kwargs: ParamType
)
Bases: ABC
Base abstract class for all AI models.
This method can be called by subclasses, but it is not mandatory.
| PARAMETER | DESCRIPTION |
|---|---|
model_config
|
The configuration for the AI model. This can be
either a
TYPE:
|
client
|
The client to use. If not provided, will be created using default_client().
TYPE:
|
get_default_client_if_not_given
|
Whether to initialize
the default client if not provided. If you want to implement
your own client setup logic, you should always set this to
TYPE:
|
api_key
|
The API key to use. If not provided, it will be read from
the
TYPE:
|
model_name
|
The name of the model to use. If provided, overrides any model name in the config.
TYPE:
|
model_type
|
The type of model to use. This is used to determine the
model name only if
TYPE:
|
**kwargs
|
Additional keyword arguments passed to default_client() when creating a new client.
TYPE:
|
Source code in conatus/models/base.py
model_config_cls
instance-attribute
¶
model_config_cls: type[ModelConfig]
The class of the model configuration.
client
instance-attribute
¶
client: ClientType
The client for the AI model.
Don't be fooled by ClientType, the
client can be of any type.
api_key_env_variable
instance-attribute
¶
api_key_env_variable: str
The environment variable that contains the API key.
model_config
instance-attribute
¶
model_config: ModelConfig = apply_config(model_config)
The configuration for the AI model.
config
property
¶
config: ModelConfig
The configuration for the model.
This is a convenience property for the model_config attribute.
with_config
¶
with_config(
model_config: ModelConfig | ModelConfigTD | None,
*,
ignore_current_config: bool = False,
inplace: bool = False
) -> Self
Return a new instance of the model with the given configuration.
This is useful for quickly creating a new model without having to instantiate a new client.
from conatus.models import OpenAIModel
from conatus.models.config import ModelConfig
model = OpenAIModel()
model_with_config = model.with_config(ModelConfig(model_name="gpt-4o"))
# Note that this also works if you pass a dictionary.
model_with_config = model.with_config({"model_name": "gpt-4o"})
assert model_with_config.config.model_name == "gpt-4o"
assert model_with_config.client == model.client
| PARAMETER | DESCRIPTION |
|---|---|
model_config
|
The configuration for the new model.
TYPE:
|
ignore_current_config
|
Whether to ignore the current configuration.
If
TYPE:
|
inplace
|
Whether to modify the current instance in place. If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Self
|
A new instance of the model with the given configuration. |
Source code in conatus/models/base.py
default_config
¶
default_config() -> ModelConfig
Return the default configuration for the model.
This method is meant to be overridden by subclasses, but it is not
mandatory; this is just a useful function for __init__.
This is also an alternative to hard-coding the configuration in the class definition, which is generally frowned upon because a dictionary is a mutable object.
Source code in conatus/models/base.py
default_model_name
classmethod
¶
Get the default model name for the model.
By default, this method returns None, which means that the model
will use the default model name for the provider. If you want to
customize behavior (e.g. so that the default model name is different
for different types of models), you can override this method.
| PARAMETER | DESCRIPTION |
|---|---|
model_type
|
The type of model to use.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
str | None
|
The default model name for the model. |
Source code in conatus/models/base.py
get_api_key
¶
get_api_key() -> str
Get the API key for the model.
This function should be implemented to retrieve environment variables.
| RETURNS | DESCRIPTION |
|---|---|
str
|
The API key. |
| RAISES | DESCRIPTION |
|---|---|
AIModelAPIKeyMissingError
|
If the API key is not found in the environment variables. |
ValueError
|
If the API key is not set in the class attribute. |
Source code in conatus/models/base.py
respawn_client
¶
Respawn the client.
This method is used to respawn the client. It is mostly used so that we can refresh the client, which might be associated with an incompatible event loop.
Source code in conatus/models/base.py
default_client
abstractmethod
¶
default_client(
model_config: ModelConfig,
api_key: str | None,
**kwargs: ParamType
) -> ParamType
Return the default client for the model.
This is a convenience method used by __init__ if the user does not
provide a client.
| PARAMETER | DESCRIPTION |
|---|---|
model_config
|
The configuration for the model.
TYPE:
|
api_key
|
The API key for the model.
TYPE:
|
**kwargs
|
Additional keyword arguments.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ParamType
|
The default client for the model. |
Source code in conatus/models/base.py
call
abstractmethod
¶
call(
prompt: AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse
call(
prompt: AIPrompt[OutputSchemaType],
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType]
call(
prompt: AIPrompt[OutputSchemaType],
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType]
Standardized call to the AI model.
The input is a AIPrompt
object,
and the output is a AIResponse
object. We use
these standardized inputs and outputs to make it easier to swap
between AI models.
If you want to implement this method, you should read carefully the
AIPrompt and
AIResponse
classes, because you will have to create the mapping between the custom
data structures of the AI provider to the standardized data structures.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to send to the AI model.
TYPE:
|
model_config
|
The configuration for the AI model. Passing a dictionary is recommended, so that users don't unintentionally re-establish default values.
TYPE:
|
printing_mixin_cls
|
The class to use for printing.
TYPE:
|
prompt_log_callback
|
A callback for debugging purposes. This callback will be called with the prompt information ( e.g. the messages, the model name, the tools, etc.) as a JSON string. |
response_log_callback_stream
|
A callback for debugging purposes. This callback will be called with the response information ( e.g. the response, the model name, the usage, etc.) as a JSON string. Note that this callback is called for each chunk of the response, and figures it out on the backend. |
**kwargs
|
Additional keyword arguments.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AIResponse[OutputSchemaType]
|
The response from the AI model. |
Source code in conatus/models/base.py
acall
abstractmethod
async
¶
acall(
prompt: AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse
acall(
prompt: AIPrompt[OutputSchemaType],
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType]
acall(
prompt: AIPrompt[OutputSchemaType] | AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType] | AIResponse
Call the AI model asynchronously.
See call for more details.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to send to the AI model.
TYPE:
|
model_config
|
The configuration for the AI model.
TYPE:
|
printing_mixin_cls
|
The class to use for printing.
TYPE:
|
prompt_log_callback
|
A callback for debugging purposes. This callback will be called with the prompt information ( e.g. the messages, the model name, the tools, etc.) as a JSON string. |
response_log_callback
|
A callback for debugging purposes. This callback will be called with the response information ( e.g. the response, the model name, the usage, etc.) as a JSON string. |
**kwargs
|
Additional keyword arguments.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AIResponse[OutputSchemaType] | AIResponse
|
The response from the AI model. |
Source code in conatus/models/base.py
call_stream
abstractmethod
¶
call_stream(
prompt: AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse
call_stream(
prompt: AIPrompt[OutputSchemaType],
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType]
call_stream(
prompt: AIPrompt[OutputSchemaType] | AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType] | AIResponse
Call the AI model and stream the response.
This method is meant to be overridden by subclasses.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to send to the AI model.
TYPE:
|
model_config
|
The configuration for the AI model.
TYPE:
|
printing_mixin_cls
|
The class to use for printing.
TYPE:
|
prompt_log_callback
|
A callback for debugging purposes. This callback will be called with the prompt information ( e.g. the messages, the model name, the tools, etc.) as a JSON string. |
response_log_callback_stream
|
A callback for debugging purposes. This callback will be called with the response information ( e.g. the response, the model name, the usage, etc.) as a JSON string. Note that this callback is called for each chunk of the response, and figures it out on the backend. |
**kwargs
|
Additional keyword arguments.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AIResponse[OutputSchemaType] | AIResponse
|
The response from the AI model. |
Source code in conatus/models/base.py
acall_stream
abstractmethod
async
¶
acall_stream(
prompt: AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse
acall_stream(
prompt: AIPrompt[OutputSchemaType],
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType]
acall_stream(
prompt: AIPrompt[OutputSchemaType] | AIPrompt,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
printing_mixin_cls: type[
AIModelPrintingMixin
] = AIModelPrintingMixin,
prompt_log_callback: (
Callable[[str], None] | None
) = None,
response_log_callback_stream: (
Callable[[str], None] | None
) = None,
**kwargs: ParamType
) -> AIResponse[OutputSchemaType] | AIResponse
Call the AI model and stream the response.
This method is meant to be overridden by subclasses.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to send to the AI model.
TYPE:
|
model_config
|
The configuration for the AI model.
TYPE:
|
printing_mixin_cls
|
The class to use for printing.
TYPE:
|
prompt_log_callback
|
A callback for debugging purposes. This callback will be called with the prompt information ( e.g. the messages, the model name, the tools, etc.) as a JSON string. |
response_log_callback_stream
|
A callback for debugging purposes. This callback will be called with the response information ( e.g. the response, the model name, the usage, etc.) as a JSON string. Note that this callback is called for each chunk of the response, and figures it out on the backend. |
**kwargs
|
Additional keyword arguments.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AIResponse[OutputSchemaType] | AIResponse
|
The response from the AI model. |
Source code in conatus/models/base.py
prepare_call_args
¶
prepare_call_args(
prompt: AIPrompt,
user_provided_config: (
ModelConfig | ModelConfigTD | None
) = None,
*,
computer_use_mode: bool = False,
previous_messages_id: str | None = None
) -> AIModelCallArgs
Prepare the arguments for the call to the AI model.
This method can be used as is, as long as you override the methods
convert_messages_to_ai_model_format
and convert_tools_to_ai_model_format
.
If the user provides a dictionary, we ensure that the values of
self.config are always
honored, and that only the values provided by the user are used.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to send to the AI model.
TYPE:
|
user_provided_config
|
The configuration for the AI model.
TYPE:
|
computer_use_mode
|
Whether to use the computer use mode.
TYPE:
|
previous_messages_id
|
The ID of the last response. If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AIModelCallArgs
|
The call arguments. See [ |
AIModelCallArgs
|
][conatus.models.base.AIModelCallArgs] for more details about the |
AIModelCallArgs
|
structure of the returned object. |
Source code in conatus/models/base.py
convert_messages_to_ai_model_format
¶
convert_messages_to_ai_model_format(
prompt: AIPrompt,
config: ModelConfig,
*,
only_new_messages: bool = False
) -> Iterable[MessageType]
Convert the messages to the AI model format.
This method is meant to be overridden by subclasses.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to convert. We pass the entire prompt in case processing on the prompt is needed (e.g. removing all the image content parts).
TYPE:
|
config
|
The configuration for the AI model.
TYPE:
|
only_new_messages
|
Whether to only convert the new messages. If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Iterable[MessageType]
|
The converted messages. (Don't be fooled by the |
Source code in conatus/models/base.py
convert_tools_to_ai_model_format
¶
convert_tools_to_ai_model_format(
prompt: AIPrompt, config: ModelConfig
) -> Iterable[ToolType] | None
Convert the tools to the AI model format.
This method is meant to be overridden by subclasses.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to convert. We pass the entire prompt in case processing on the tools is needed (e.g. removing certain incompatible tools).
TYPE:
|
config
|
The configuration for the AI model.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Iterable[ToolType] | None
|
The converted tools. (Don't be fooled by the |
Source code in conatus/models/base.py
convert_system_message_to_ai_model_format
¶
convert_system_message_to_ai_model_format(
system_message: SystemAIMessage, config: ModelConfig
) -> MessageType
Convert the system message to the AI model format.
This method is meant to be overridden by subclasses.
| PARAMETER | DESCRIPTION |
|---|---|
system_message
|
The system message to convert.
TYPE:
|
config
|
The configuration for the AI model.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MessageType
|
The converted system message. |
Source code in conatus/models/base.py
convert_output_schema_to_ai_model_format
¶
convert_output_schema_to_ai_model_format(
prompt: AIPrompt, config: ModelConfig
) -> (
tuple[OutputJSONSchemaType, bool]
| tuple[None, Literal[False]]
)
Convert the output schema to the AI model format.
This method is meant to be overridden by subclasses.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to convert.
TYPE:
|
config
|
The configuration for the AI model.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple[OutputJSONSchemaType, bool] | tuple[None, Literal[False]]
|
If the AI model requires an output schema, return a tuple with the
output schema and a boolean indicating whether the schema has
been converted from a non-object type to an object type. This is
sometimes necessary because the AI model requires an object type
and the output schema is a string or a list. In this case, the
boolean should be |
Source code in conatus/models/base.py
simple_call
¶
simple_call(
prompt: str,
model_config: ModelConfig | ModelConfigTD | None = None,
*,
stream: bool = False
) -> str
Simple call to the AI model.
This is a convenience method for the call method.
from conatus.models import OpenAIModel
model = OpenAIModel()
q = "Which US state has never recorded temperatures below 0°F?"
response = model.simple_call(q)
# > That would be Hawaii.
| PARAMETER | DESCRIPTION |
|---|---|
prompt
|
The prompt to send to the AI model.
TYPE:
|
model_config
|
The configuration for the AI model. Passing a dictionary is recommended, so that users don't unintentionally re-establish default values.
TYPE:
|
stream
|
Whether to stream the response. If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
str
|
The response from the AI model. |
Source code in conatus/models/base.py
Type utilities¶
These are type utilities that are used in the BaseAIModel
. They are useful to understand when
implementing a subclass.
conatus.models.base.ClientType
module-attribute
¶
Alias for the type of the client.
This is a convenience type alias to make the code more readable.
conatus.models.base.MessageType
module-attribute
¶
Alias for the type of messages expected by the model.
For example, OpenAI's chat.completions.create function expects an
iterable of this type for its messages argument.
This can be anything, and is a convenience type alias to make the code more readable.
conatus.models.base.ToolType
module-attribute
¶
Alias for the type of the tools expected by the model.
For example, OpenAI's chat.completions.create function expects an
iterable of this type for its tools argument.
This can be anything, and is a convenience type alias to make the code more readable.