OpenResponses.Adapter behaviour (OpenResponses v0.1.1)

View Source

Behaviour that all provider adapters must implement.

An adapter is responsible for three things:

  1. Building the request — translate the loop state into the provider's native request format.
  2. Streaming — make the HTTP request and return a lazy Stream of raw provider-native events.
  3. Normalising events — translate each raw provider event into an Open Responses spec event map.

API key handling

API keys are supplied per-request by the caller in the "api_key" field of the request body. The server never uses a shared key for client requests. The resolved key is passed to stream/2 via the config keyword list as api_key: "sk-...".

Server-side config (via config :open_responses, :provider_config) can set a base_url override or other adapter-specific options, but the api_key from the request always takes precedence.

Built-in adapters

ModuleProvider
OpenResponses.Adapters.OpenAIOpenAI
OpenResponses.Adapters.AnthropicAnthropic / z.ai
OpenResponses.Adapters.GeminiGoogle Gemini
OpenResponses.Adapters.OllamaOllama (local)
OpenResponses.Adapters.MockTesting

Writing a custom adapter

defmodule MyApp.Adapters.MyProvider do
  @behaviour OpenResponses.Adapter

  @impl OpenResponses.Adapter
  def build_request(%{response: response, input: input}) do
    %{model: response.model, messages: input}
  end

  @impl OpenResponses.Adapter
  def stream(request, config) do
    api_key = Keyword.fetch!(config, :api_key)
    {:ok, MyProvider.stream(request, api_key)}
  end

  @impl OpenResponses.Adapter
  def normalize_event(%{"type" => "my.delta", "text" => t}),
    do: %{"type" => "response.output_text.delta", "delta" => t}
  def normalize_event(%{"type" => "my.done"}),
    do: %{"type" => "response.completed"}
  def normalize_event(event), do: event
end

Register it via routing config:

config :open_responses, :routing, %{
  ~r/^myprovider-/ => MyApp.Adapters.MyProvider
}

See the Providers guide for full details.

Summary

Types

config()

@type config() :: keyword()

event()

@type event() :: map()

request()

@type request() :: map()

Callbacks

build_request(loop_state)

@callback build_request(loop_state :: map()) :: request()

normalize_event(provider_event)

@callback normalize_event(provider_event :: map()) :: event()

stream(request, config)

@callback stream(request(), config()) :: {:ok, Enumerable.t()} | {:error, term()}