| class OpenAIChatCompletionClient(BaseOpenAIChatCompletionClient, Component[OpenAIClientConfigurationConfigModel]): |
| """Chat completion client for OpenAI hosted models. |
| |
| To use this client, you must install the `openai` extra: |
| |
| .. code-block:: bash |
| |
| pip install "autogen-ext[openai]" |
| |
| You can also use this client for OpenAI-compatible ChatCompletion endpoints. |
| **Using this client for non-OpenAI models is not tested or guaranteed.** |
| |
| For non-OpenAI models, please first take a look at our `community extensions <https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/index.html>`_ |
| for additional model clients. |
| |
| Args: |
| model (str): Which OpenAI model to use. |
| api_key (optional, str): The API key to use. **Required if 'OPENAI_API_KEY' is not found in the environment variables.** |
| organization (optional, str): The organization ID to use. |
| base_url (optional, str): The base URL to use. **Required if the model is not hosted on OpenAI.** |
| timeout: (optional, float): The timeout for the request in seconds. |
| max_retries (optional, int): The maximum number of retries to attempt. |
| model_info (optional, ModelInfo): The capabilities of the model. **Required if the model name is not a valid OpenAI model.** |
| frequency_penalty (optional, float): |
| logit_bias: (optional, dict[str, int]): |
| max_tokens (optional, int): |
| n (optional, int): |
| presence_penalty (optional, float): |
| response_format (optional, Dict[str, Any]): the format of the response. Possible options are: |
| |
| .. code-block:: text |
| |
| # Text response, this is the default. |
| {"type": "text"} |
| |
| .. code-block:: text |
| |
| # JSON response, make sure to instruct the model to return JSON. |
| {"type": "json_object"} |
| |
| .. code-block:: text |
| |
| # Structured output response, with a pre-defined JSON schema. |
| { |
| "type": "json_schema", |
| "json_schema": { |
| "name": "name of the schema, must be an identifier.", |
| "description": "description for the model.", |
| # You can convert a Pydantic (v2) model to JSON schema |
| # using the `model_json_schema()` method. |
| "schema": "<the JSON schema itself>", |
| # Whether to enable strict schema adherence when |
| # generating the output. If set to true, the model will |
| # always follow the exact schema defined in the |
| # `schema` field. Only a subset of JSON Schema is |
| # supported when `strict` is `true`. |
| # To learn more, read |
| # https://platform.openai.com/docs/guides/structured-outputs. |
| "strict": False, # or True |
| }, |
| } |
| |
| It is recommended to use the `json_output` parameter in |
| :meth:`~autogen_ext.models.openai.BaseOpenAIChatCompletionClient.create` or |
| :meth:`~autogen_ext.models.openai.BaseOpenAIChatCompletionClient.create_stream` |
| methods instead of `response_format` for structured output. |
| The `json_output` parameter is more flexible and allows you to |
| specify a Pydantic model class directly. |
| |
| seed (optional, int): |
| stop (optional, str | List[str]): |
| temperature (optional, float): |
| top_p (optional, float): |
| parallel_tool_calls (optional, bool): Whether to allow parallel tool calls. When not set, defaults to server behavior. |
| user (optional, str): |
| default_headers (optional, dict[str, str]): Custom headers; useful for authentication or other custom requirements. |
| add_name_prefixes (optional, bool): Whether to prepend the `source` value |
| to each :class:`~autogen_core.models.UserMessage` content. E.g., |
| "this is content" becomes "Reviewer said: this is content." |
| This can be useful for models that do not support the `name` field in |
| message. Defaults to False. |
| include_name_in_message (optional, bool): Whether to include the `name` field |
| in user message parameters sent to the OpenAI API. Defaults to True. Set to False |
| for model providers that don't support the `name` field (e.g., Groq). |
| stream_options (optional, dict): Additional options for streaming. Currently only `include_usage` is supported. |
| |
| Examples: |
| |
| The following code snippet shows how to use the client with an OpenAI model: |
| |
| .. code-block:: python |
| |
| from autogen_ext.models.openai import OpenAIChatCompletionClient |
| from autogen_core.models import UserMessage |
| |
| openai_client = OpenAIChatCompletionClient( |
| model="gpt-4o-2024-08-06", |
| # api_key="sk-...", # Optional if you have an OPENAI_API_KEY environment variable set. |
| ) |
| |
| result = await openai_client.create([UserMessage(content="What is the capital of France?", source="user")]) # type: ignore |
| print(result) |
| |
| # Close the client when done. |
| # await openai_client.close() |
| |
| To use the client with a non-OpenAI model, you need to provide the base URL of the model and the model info. |
| For example, to use Ollama, you can use the following code snippet: |
| |
| .. code-block:: python |
| |
| from autogen_ext.models.openai import OpenAIChatCompletionClient |
| from autogen_core.models import ModelFamily |
| |
| custom_model_client = OpenAIChatCompletionClient( |
| model="deepseek-r1:1.5b", |
| base_url="http://localhost:11434/v1", |
| api_key="placeholder", |
| model_info={ |
| "vision": False, |
| "function_calling": False, |
| "json_output": False, |
| "family": ModelFamily.R1, |
| "structured_output": True, |
| }, |
| ) |
| |
| # Close the client when done. |
| # await custom_model_client.close() |
| |
| To use streaming mode, you can use the following code snippet: |
| |
| .. code-block:: python |
| |
| import asyncio |
| from autogen_core.models import UserMessage |
| from autogen_ext.models.openai import OpenAIChatCompletionClient |
| |
| |
| async def main() -> None: |
| # Similar for AzureOpenAIChatCompletionClient. |
| model_client = OpenAIChatCompletionClient(model="gpt-4o") # assuming OPENAI_API_KEY is set in the environment. |
| |
| messages = [UserMessage(content="Write a very short story about a dragon.", source="user")] |
| |
| # Create a stream. |
| stream = model_client.create_stream(messages=messages) |
| |
| # Iterate over the stream and print the responses. |
| print("Streamed responses:") |
| async for response in stream: |
| if isinstance(response, str): |
| # A partial response is a string. |
| print(response, flush=True, end="") |
| else: |
| # The last response is a CreateResult object with the complete message. |
| print("\\n\\n------------\\n") |
| print("The complete response:", flush=True) |
| print(response.content, flush=True) |
| |
| # Close the client when done. |
| await model_client.close() |
| |
| |
| asyncio.run(main()) |
| |
| To use structured output as well as function calling, you can use the following code snippet: |
| |
| .. code-block:: python |
| |
| import asyncio |
| from typing import Literal |
| |
| from autogen_core.models import ( |
| AssistantMessage, |
| FunctionExecutionResult, |
| FunctionExecutionResultMessage, |
| SystemMessage, |
| UserMessage, |
| ) |
| from autogen_core.tools import FunctionTool |
| from autogen_ext.models.openai import OpenAIChatCompletionClient |
| from pydantic import BaseModel |
| |
| |
| # Define the structured output format. |
| class AgentResponse(BaseModel): |
| thoughts: str |
| response: Literal["happy", "sad", "neutral"] |
| |
| |
| # Define the function to be called as a tool. |
| def sentiment_analysis(text: str) -> str: |
| \"\"\"Given a text, return the sentiment.\"\"\" |
| return "happy" if "happy" in text else "sad" if "sad" in text else "neutral" |
| |
| |
| # Create a FunctionTool instance with `strict=True`, |
| # which is required for structured output mode. |
| tool = FunctionTool(sentiment_analysis, description="Sentiment Analysis", strict=True) |
| |
| |
| async def main() -> None: |
| # Create an OpenAIChatCompletionClient instance. |
| model_client = OpenAIChatCompletionClient(model="gpt-4o-mini") |
| |
| # Generate a response using the tool. |
| response1 = await model_client.create( |
| messages=[ |
| SystemMessage(content="Analyze input text sentiment using the tool provided."), |
| UserMessage(content="I am happy.", source="user"), |
| ], |
| tools=[tool], |
| ) |
| print(response1.content) |
| # Should be a list of tool calls. |
| # [FunctionCall(name="sentiment_analysis", arguments={"text": "I am happy."}, ...)] |
| |
| assert isinstance(response1.content, list) |
| response2 = await model_client.create( |
| messages=[ |
| SystemMessage(content="Analyze input text sentiment using the tool provided."), |
| UserMessage(content="I am happy.", source="user"), |
| AssistantMessage(content=response1.content, source="assistant"), |
| FunctionExecutionResultMessage( |
| content=[FunctionExecutionResult(content="happy", call_id=response1.content[0].id, is_error=False, name="sentiment_analysis")] |
| ), |
| ], |
| # Use the structured output format. |
| json_output=AgentResponse, |
| ) |
| print(response2.content) |
| # Should be a structured output. |
| # {"thoughts": "The user is happy.", "response": "happy"} |
| |
| # Close the client when done. |
| await model_client.close() |
| |
| asyncio.run(main()) |
| |
| |
| To load the client from a configuration, you can use the `load_component` method: |
| |
| .. code-block:: python |
| |
| from autogen_core.models import ChatCompletionClient |
| |
| config = { |
| "provider": "OpenAIChatCompletionClient", |
| "config": {"model": "gpt-4o", "api_key": "REPLACE_WITH_YOUR_API_KEY"}, |
| } |
| |
| client = ChatCompletionClient.load_component(config) |
| |
| To view the full list of available configuration options, see the :py:class:`OpenAIClientConfigurationConfigModel` class. |
| |
| """ |
|
|
| component_type = "model" |
| component_config_schema = OpenAIClientConfigurationConfigModel |
| component_provider_override = "autogen_ext.models.openai.OpenAIChatCompletionClient" |
|
|
| def __init__(self, **kwargs: Unpack[OpenAIClientConfiguration]): |
| if "model" not in kwargs: |
| raise ValueError("model is required for OpenAIChatCompletionClient") |
|
|
| model_capabilities: Optional[ModelCapabilities] = None |
| self._raw_config: Dict[str, Any] = dict(kwargs).copy() |
| copied_args = dict(kwargs).copy() |
|
|
| if "model_capabilities" in kwargs: |
| model_capabilities = kwargs["model_capabilities"] |
| del copied_args["model_capabilities"] |
|
|
| model_info: Optional[ModelInfo] = None |
| if "model_info" in kwargs: |
| model_info = kwargs["model_info"] |
| del copied_args["model_info"] |
|
|
| add_name_prefixes: bool = False |
| if "add_name_prefixes" in kwargs: |
| add_name_prefixes = kwargs["add_name_prefixes"] |
|
|
| include_name_in_message: bool = True |
| if "include_name_in_message" in kwargs: |
| include_name_in_message = kwargs["include_name_in_message"] |
|
|
| |
| assert "model" in copied_args and isinstance(copied_args["model"], str) |
| if copied_args["model"].startswith("gemini-"): |
| if "base_url" not in copied_args: |
| copied_args["base_url"] = _model_info.GEMINI_OPENAI_BASE_URL |
| if "api_key" not in copied_args and "GEMINI_API_KEY" in os.environ: |
| copied_args["api_key"] = os.environ["GEMINI_API_KEY"] |
| if copied_args["model"].startswith("claude-"): |
| if "base_url" not in copied_args: |
| copied_args["base_url"] = _model_info.ANTHROPIC_OPENAI_BASE_URL |
| if "api_key" not in copied_args and "ANTHROPIC_API_KEY" in os.environ: |
| copied_args["api_key"] = os.environ["ANTHROPIC_API_KEY"] |
| if copied_args["model"].startswith("Llama-"): |
| if "base_url" not in copied_args: |
| copied_args["base_url"] = _model_info.LLAMA_API_BASE_URL |
| if "api_key" not in copied_args and "LLAMA_API_KEY" in os.environ: |
| copied_args["api_key"] = os.environ["LLAMA_API_KEY"] |
|
|
| client = _openai_client_from_config(copied_args) |
| create_args = _create_args_from_config(copied_args) |
|
|
| super().__init__( |
| client=client, |
| create_args=create_args, |
| model_capabilities=model_capabilities, |
| model_info=model_info, |
| add_name_prefixes=add_name_prefixes, |
| include_name_in_message=include_name_in_message, |
| ) |
|
|
| def __getstate__(self) -> Dict[str, Any]: |
| state = self.__dict__.copy() |
| state["_client"] = None |
| return state |
|
|
| def __setstate__(self, state: Dict[str, Any]) -> None: |
| self.__dict__.update(state) |
| self._client = _openai_client_from_config(state["_raw_config"]) |
|
|
| def _to_config(self) -> OpenAIClientConfigurationConfigModel: |
| copied_config = self._raw_config.copy() |
| return OpenAIClientConfigurationConfigModel(**copied_config) |
|
|
| @classmethod |
| def _from_config(cls, config: OpenAIClientConfigurationConfigModel) -> Self: |
| copied_config = config.model_copy().model_dump(exclude_none=True) |
|
|
| |
| if "api_key" in copied_config and isinstance(config.api_key, SecretStr): |
| copied_config["api_key"] = config.api_key.get_secret_value() |
|
|
| return cls(**copied_config) |
|
|
|
|