View Source LangChain.ChatModels.ChatOpenAI (LangChain v0.3.3)

Represents the OpenAI ChatModel.

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

Converts responses into more specialized LangChain data structures.

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. That data can be accessed using the LLM callback on_llm_token_usage like this:

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

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

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

%LangChain.TokenUsage{input: 15, output: 3}

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

Tool Choice

OpenAI's ChatGPT API supports forcing a tool to be used.

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.

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 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.

Summary

Functions

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.

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

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.

Return the params formatted for an API request.

Setup a ChatOpenAI client configuration.

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

Restores the model from the config.

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

Types

@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(),
  reasoning_effort: term(),
  reasoning_mode: term(),
  receive_timeout: term(),
  seed: term(),
  stream: term(),
  stream_options: term(),
  temperature: term(),
  tool_choice: term(),
  user: term(),
  verbose_api: term()
}

Functions

Link to this function

call(openai, prompt, tools \\ [])

View Source

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.

Link to this function

decode_stream(arg, done \\ [])

View Source

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.

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.

Link to this function

for_api(openai, messages, tools)

View Source
@spec for_api(
  t() | LangChain.Message.t() | LangChain.Function.t(),
  message :: [map()],
  LangChain.ChatModels.ChatModel.tools()
) :: %{required(atom()) => any()}

Return the params formatted for an API request.

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

Setup a ChatOpenAI client configuration.

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

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

Restores the model from the config.

@spec serialize_config(t()) :: %{required(String.t()) => any()}

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