Any module that implements the LlmComposer.Provider behaviour can be used as a provider.
Required Callbacks
@callback name() :: atom()
@callback run([Message.t()], Message.t() | nil, keyword()) ::
{:ok, LlmResponse.t()} | {:error, term()}name/0— returns an atom identifying your provider (e.g.:my_provider).run/3— executes a completion request and returns a normalizedLlmResponse.
Minimal Implementation
defmodule MyApp.Providers.MyProvider do
@behaviour LlmComposer.Provider
alias LlmComposer.LlmResponse
alias LlmComposer.Message
@impl LlmComposer.Provider
def name, do: :my_provider
@impl LlmComposer.Provider
def run(messages, system_message, opts) do
model = Keyword.fetch!(opts, :model)
api_key = Keyword.fetch!(opts, :api_key)
body = build_request(messages, system_message, model)
case call_api(body, api_key) do
{:ok, raw} ->
response =
LlmResponse.new(%{
status: :ok,
main_response: Message.new(:assistant, raw["text"]),
provider: name(),
provider_model: model,
raw: raw
})
{:ok, response}
{:error, reason} ->
{:error, reason}
end
end
endRegistration
Pass your module via :provider (single) or :providers (multi-provider routing):
# Single provider
%LlmComposer.Settings{
provider: MyApp.Providers.MyProvider,
provider_opts: [model: "my-model", api_key: "..."]
}
# Multi-provider routing
%LlmComposer.Settings{
providers: [
{MyApp.Providers.MyProvider, [model: "my-model", api_key: "..."]}
]
}Optional: Response Normalization Adapter
For complex response shapes, implement a LlmComposer.ProviderResponse.* adapter instead of
building the LlmResponse inline. See the existing provider response modules in
lib/llm_composer/provider_response/ for the pattern.
Optional: Streaming Support
To support streaming, implement a LlmComposer.ProviderStreamChunk.* module using the
Struct macro:
defmodule MyApp.Providers.MyProvider.StreamChunk do
use LlmComposer.ProviderStreamChunk.Struct,
parser: MyApp.Providers.MyProvider.StreamChunk.Parser,
provider: :my_provider
end
defmodule MyApp.Providers.MyProvider.StreamChunk.Parser do
def parse(chunk_map, _provider, _opts) do
# Map provider-specific chunk map to LlmComposer.StreamChunk fields
{:ok, %LlmComposer.StreamChunk{type: :text_delta, text: chunk_map["delta"]}}
end
end