# `LangChain.ChatModels.ChatOpenAI`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L1)

Represents the [OpenAI
ChatModel](https://platform.openai.com/docs/api-reference/chat/create).

Parses and validates inputs for making a requests from the OpenAI Chat API.

Converts responses into more specialized `LangChain` data structures.

- https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb

## ContentPart Types

OpenAI supports several types of content parts that can be combined in a single message:

### Text Content
Basic text content is the default and most common type:

    Message.new_user!("Hello, how are you?")

### Image Content
OpenAI supports both base64-encoded images and image URLs:

    # Using a base64 encoded image
    Message.new_user!([
      ContentPart.text!("What's in this image?"),
      ContentPart.image!("base64_encoded_image_data", media: :jpg)
    ])

    # Using an image URL
    Message.new_user!([
      ContentPart.text!("Describe this image:"),
      ContentPart.image_url!("https://example.com/image.jpg")
    ])

For images, you can specify the detail level which affects token usage:
- `detail: "low"` - Lower resolution, fewer tokens
- `detail: "high"` - Higher resolution, more tokens
- `detail: "auto"` - Let the model decide

### File Content
OpenAI supports both base64-encoded files and file IDs:

    # Using a base64 encoded file
    Message.new_user!([
      ContentPart.text!("Process this file:"),
      ContentPart.file!("base64_encoded_file_data",
        type: :base64,
        filename: "document.pdf"
      )
    ])

    # Using a file ID (after uploading to OpenAI)
    Message.new_user!([
      ContentPart.text!("Process this file:"),
      ContentPart.file!("file-1234", type: :file_id)
    ])

## Callbacks

See the set of available callbacks: `LangChain.Chains.ChainCallbacks`

### Rate Limit API Response Headers

OpenAI returns rate limit information in the response headers. Those can be
accessed using the LLM callback `on_llm_ratelimit_info` like this:

    handlers = %{
      on_llm_ratelimit_info: fn _model, headers ->
        IO.inspect(headers)
      end
    }

    {:ok, chat} = ChatOpenAI.new(%{callbacks: [handlers]})

When a request is received, something similar to the following will be output
to the console.

    %{
      "x-ratelimit-limit-requests" => ["5000"],
      "x-ratelimit-limit-tokens" => ["160000"],
      "x-ratelimit-remaining-requests" => ["4999"],
      "x-ratelimit-remaining-tokens" => ["159973"],
      "x-ratelimit-reset-requests" => ["12ms"],
      "x-ratelimit-reset-tokens" => ["10ms"],
      "x-request-id" => ["req_1234"]
    }

### Token Usage

OpenAI returns token usage information as part of the response body. The
`LangChain.TokenUsage` is added to the `metadata` of the `LangChain.Message`
and `LangChain.MessageDelta` structs that are processed under the `:usage`
key.

The OpenAI documentation instructs to provide the `stream_options` with the
`include_usage: true` for the information to be provided.

```elixir
chat = ChatOpenAI.new!(%{stream: true, stream_options: %{include_usage: true}})
```

The `TokenUsage` data is accumulated for `MessageDelta` structs and the final usage information will be on the `LangChain.Message`.

NOTE: Of special note is that the `TokenUsage` information is returned once
for all "choices" in the response. The `LangChain.TokenUsage` data is added to
each message, but if your usage requests multiple choices, you will see the
same usage information for each choice but it is duplicated and only one
response is meaningful.

## Tool Choice

OpenAI's ChatGPT API supports forcing a tool to be used.
- https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice

This is supported through the `tool_choice` options. It takes a plain Elixir
map to provide the configuration.

By default, the LLM will choose a tool call if a tool is available and it
determines it is needed. That's the "auto" mode.

## Parallel Tool Calls

By default, OpenAI models may decide to make multiple tool calls at once,
including calling the same tool multiple times. You can limit this behavior by
setting the `parallel_tool_calls`
[option](https://platform.openai.com/docs/api-reference/chat/create#chat_create-parallel_tool_calls)
to false.

### Example
For the LLM's response to make a tool call of the "get_weather" function.

    ChatOpenAI.new(%{
      model: "...",
      tool_choice: %{"type" => "function", "function" => %{"name" => "get_weather"}}
    })

## Azure OpenAI Support

To use `ChatOpenAI` with Microsoft's Azure hosted OpenAI models, the
`endpoint` must be overridden and the API key needs to be provided in some
way. The [MS Quickstart guide for REST
access](https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line%2Cjavascript-keyless%2Ctypescript-keyless%2Cpython-new&pivots=rest-api)
may be helpful.

In order to use it, you must have an Azure account and from the console, a
model must be deployed for your account. Use the Azure AI Foundry and Azure
OpenAI Service to deploy the model you want to use. The entire URL is used as
the `endpoint` and the provided `key` is used as the `api_key`.

The following is an example of setting up `ChatOpenAI` for use with an Azure
hosted model.

    endpoint = System.fetch_env!("AZURE_OPENAI_ENDPOINT")
    api_key = System.fetch_env!("AZURE_OPENAI_KEY")

    llm =
      ChatOpenAI.new!(%{
        endpoint: endpoint,
        api_key: api_key,
        seed: 0,
        temperature: 1,
        stream: false
      })

The URL itself specifies the model to use and the `model` attribute is
disregarded.

A fake example URL for the endpoint value:

`https://some-subdomain.cognitiveservices.azure.com/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-08-01-preview"`

## Reasoning Model Support

OpenAI made some significant API changes with the introduction of their
"reasoning" models. This includes the `o1` and `o1-mini` models.

To enable this mode, set `:reasoning_mode` to `true`:

    model = ChatOpenAI.new!(%{reasoning_mode: true})

Setting `reasoning_mode` to `true` does at least the two following things:

- Set `:developer` as the `role` for system messages. The OpenAI documentation
  says API calls to `o1` and newer models must use the `role: :developer`
  instead of `role: :system` and errors if not set correctly.
- The `:reasoning_effort` option included in LLM requests. This setting is
  only permitted on a reasoning model. The `:reasoning_effort` values support
  the "low", "medium" (default), and "high" options specified in the OpenAI
  documentation. This instructs the LLM on how much time, and tokens, should
  be spent on thinking through and reasoning about the request and the
  response.

# `t`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L305)

```elixir
@type t() :: %LangChain.ChatModels.ChatOpenAI{
  api_key: term(),
  callbacks: term(),
  endpoint: term(),
  frequency_penalty: term(),
  json_response: term(),
  json_schema: term(),
  max_tokens: term(),
  model: term(),
  n: term(),
  org_id: term(),
  parallel_tool_calls: term(),
  reasoning_effort: term(),
  reasoning_mode: term(),
  receive_timeout: term(),
  req_config: term(),
  seed: term(),
  stream: term(),
  stream_options: term(),
  temperature: term(),
  tool_choice: term(),
  user: term(),
  verbose_api: term(),
  verbosity: term()
}
```

# `call`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L695)

Calls the OpenAI API passing the ChatOpenAI struct with configuration, plus
either a simple message or the list of messages to act as the prompt.

Optionally pass in a list of tools available to the LLM for requesting
execution in response.

Optionally pass in a callback function that can be executed as data is
received from the API.

**NOTE:** This function *can* be used directly, but the primary interface
should be through `LangChain.Chains.LLMChain`. The `ChatOpenAI` module is more
focused on translating the `LangChain` data structures to and from the OpenAI
API.

Another benefit of using `LangChain.Chains.LLMChain` is that it combines the
storage of messages, adding tools, adding custom context that should be
passed to tools, and automatically applying `LangChain.MessageDelta`
structs as they are are received, then converting those to the full
`LangChain.Message` once fully complete.

# `content_part_for_api`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L587)

Convert a ContentPart to the expected map of data for the OpenAI API.

# `content_parts_for_api`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L580)

Convert a list of ContentParts to the expected map of data for the OpenAI API.

# `decode_stream`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L931)

```elixir
@spec decode_stream(
  {String.t(), String.t()},
  list()
) :: {%{required(String.t()) =&gt; any()}}
```

Decode a streamed response from an OpenAI-compatible server. Parses a string
of received content into an Elixir map data structure using string keys.

If a partial response was received, meaning the JSON text is split across
multiple data frames, then the incomplete portion is returned as-is in the
buffer. The function will be successively called, receiving the incomplete
buffer data from a previous call, and assembling it to parse.

# `for_api`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L493)

```elixir
@spec for_api(
  struct(),
  LangChain.Message.t()
  | LangChain.PromptTemplate.t()
  | LangChain.Message.ToolCall.t()
  | LangChain.Message.ToolResult.t()
  | LangChain.Message.ContentPart.t()
  | LangChain.Function.t()
) :: %{required(String.t()) =&gt; any()} | [%{required(String.t()) =&gt; any()}]
```

Convert a LangChain Message-based structure to the expected map of data for
the OpenAI API. This happens within the context of the model configuration as
well. The additional context is needed to correctly convert a role to either
`:system` or `:developer`.

NOTE: The `ChatOpenAI` model's functions are reused in other modules. For this
reason, model is more generally defined as a struct.

# `for_api`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L388)

```elixir
@spec for_api(
  t() | LangChain.Message.t() | LangChain.Function.t(),
  message :: [map()],
  LangChain.ChatModels.ChatModel.tools()
) :: %{required(atom()) =&gt; any()}
```

Return the params formatted for an API request.

# `new`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L352)

```elixir
@spec new(attrs :: map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
```

Setup a ChatOpenAI client configuration.

# `new!`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L363)

```elixir
@spec new!(attrs :: map()) :: t() | no_return()
```

Setup a ChatOpenAI client configuration and return it or raise an error if invalid.

# `restore_from_map`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L1311)

Restores the model from the config.

# `retry_on_fallback?`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L1273)

```elixir
@spec retry_on_fallback?(LangChain.LangChainError.t()) :: boolean()
```

Determine if an error should be retried. If `true`, a fallback LLM may be
used. If `false`, the error is understood to be more fundamental with the
request rather than a service issue and it should not be retried or fallback
to another service.

# `serialize_config`
[🔗](https://github.com/brainlid/langchain/blob/v0.6.2/lib/chat_models/chat_open_ai.ex#L1284)

```elixir
@spec serialize_config(t()) :: %{required(String.t()) =&gt; any()}
```

Generate a config map that can later restore the model's configuration.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
