Behaviour for LLM providers.
A provider is an interchangeable service (OpenAI, Gemini, DeepSeek, ...)
implementing a shared capability. Each provider is a struct module that
declares @behaviour ExAgent.Provider and implements chat/3 (required)
and optionally upload/4. The provider struct carries its own config
(api key, model, cached Req client) and is passed as the first argument.
This module also acts as the dispatcher: it forwards to the callback module resolved from the provider struct.
Implementations
Extensibility
To add a new provider, define a struct and implement this behaviour:
defmodule MyApp.Providers.CustomLLM do
@behaviour ExAgent.Provider
defstruct [:api_key, :model, :base_url, :system_prompt, :req, tools: []]
@impl true
def chat(provider, messages, opts) do
# Your implementation here
end
end
Summary
Callbacks
Sends a list of messages to the LLM and returns the assistant's response.
Streams the assistant's response as a lazy enumerable of text chunks.
Uploads binary file data to the provider and returns a file reference.
Functions
Dispatches a chat request to the provider's implementation.
Dispatches a streaming chat request to the provider's implementation.
Dispatches a file upload to the provider's implementation.
Callbacks
@callback chat(provider :: struct(), [ExAgent.Message.t()], keyword()) :: {:ok, ExAgent.Message.t()} | {:tool_call, String.t(), map()} | {:error, term()}
Sends a list of messages to the LLM and returns the assistant's response.
Returns {:ok, message} for a regular response, {:tool_call, name, args}
when the LLM wants to invoke a tool, or {:error, reason} on failure.
@callback stream(provider :: struct(), [ExAgent.Message.t()], keyword()) :: Enumerable.t()
Streams the assistant's response as a lazy enumerable of text chunks.
Optional — providers without streaming support omit this callback.
@callback upload(provider :: struct(), binary(), String.t(), keyword()) :: {:ok, ExAgent.FileRef.t()} | {:error, term()}
Uploads binary file data to the provider and returns a file reference.
Optional — providers without file support (e.g. DeepSeek) omit this callback.
Functions
@spec chat(struct(), [ExAgent.Message.t()], keyword()) :: {:ok, ExAgent.Message.t()} | {:tool_call, String.t(), map()} | {:error, term()}
Dispatches a chat request to the provider's implementation.
@spec stream(struct(), [ExAgent.Message.t()], keyword()) :: Enumerable.t()
Dispatches a streaming chat request to the provider's implementation.
Returns a lazy enumerable of text chunks. Raises ArgumentError if the
provider does not implement stream/3.
@spec upload(struct(), binary(), String.t(), keyword()) :: {:ok, ExAgent.FileRef.t()} | {:error, term()}
Dispatches a file upload to the provider's implementation.
Returns {:error, {:unsupported, :upload, module}} if the provider does not
implement upload/4.