AgentSessionManager.Ports.ProviderAdapter behaviour (AgentSessionManager v0.8.0)

Copy Markdown View Source

Port (interface) for AI provider adapters.

This behaviour defines the contract that all provider adapter implementations must fulfill. It follows the ports and adapters pattern, allowing different AI providers (Anthropic, OpenAI, etc.) to be swapped without changing the core business logic.

Design Principles

  • Provider-agnostic: Core logic doesn't depend on provider specifics
  • Capability-based: Adapters declare what they support
  • Event-driven: Execution emits normalized events via callbacks
  • Cancellable: Long-running operations can be cancelled

Implementation Requirements

Implementations must:

  1. Return a unique provider name
  2. Declare supported capabilities
  3. Execute runs and emit events via callback
  4. Support cancellation of in-progress runs
  5. Validate provider-specific configuration

Event Emission

During execute/4, adapters should call the :event_callback option (if provided) with normalized event maps containing:

  • :type - Event type (e.g., :run_started, :message_received)
  • :session_id - The session ID
  • :run_id - The run ID
  • :data - Event-specific payload
  • :timestamp - When the event occurred

Usage

Adapters are typically used through the SessionManager:

# The SessionManager handles adapter lifecycle
{:ok, manager} = SessionManager.start_link(
  adapter: MyAdapter,
  adapter_opts: [api_key: "..."]
)

# Or directly for testing
{:ok, adapter} = MyAdapter.start_link(api_key: "...")
{:ok, capabilities} = ProviderAdapter.capabilities(adapter)
{:ok, result} = ProviderAdapter.execute(adapter, run, session, event_callback: fn e -> ... end)

Summary

Callbacks

Cancels an in-progress run.

Returns the list of capabilities supported by this provider.

Executes a run against the AI provider.

Returns the unique name of this provider.

Validates provider-specific configuration.

Functions

Cancels a run.

Returns the list of capabilities.

Returns the provider name.

Resolves execute call timeout from options, including a grace period for asynchronous result handoff back to callers.

Validates configuration.

Types

adapter()

@type adapter() :: GenServer.server() | pid() | atom() | module()

execute_opts()

@type execute_opts() :: [
  event_callback: (map() -> any()),
  timeout: pos_integer() | :unbounded | :infinity | String.t()
]

run_result()

@type run_result() :: %{output: map(), token_usage: map(), events: [map()]}

Callbacks

cancel(adapter, t)

@callback cancel(adapter(), String.t()) ::
  {:ok, String.t()} | {:error, AgentSessionManager.Core.Error.t()}

Cancels an in-progress run.

Providers should attempt to gracefully cancel the run. After cancellation, the run should emit a :run_cancelled event.

Parameters

  • adapter - The adapter instance
  • run_id - The ID of the run to cancel

Returns

  • {:ok, run_id} - Cancellation initiated (run will emit cancelled event)
  • {:error, Error.t()} - Cancellation failed

Examples

iex> MyAdapter.cancel(adapter, "run_123")
{:ok, "run_123"}

capabilities(adapter)

@callback capabilities(adapter()) ::
  {:ok, [AgentSessionManager.Core.Capability.t()]}
  | {:error, AgentSessionManager.Core.Error.t()}

Returns the list of capabilities supported by this provider.

Capabilities define what the provider can do - tools, resources, sampling modes, etc. The SessionManager uses this to validate that required capabilities are available before starting runs.

Returns

  • {:ok, [Capability.t()]} - List of supported capabilities
  • {:error, Error.t()} - If capabilities cannot be determined

Examples

iex> MyAdapter.capabilities(adapter)
{:ok, [
  %Capability{name: "chat", type: :tool, enabled: true},
  %Capability{name: "sampling", type: :sampling, enabled: true}
]}

execute(adapter, t, t, execute_opts)

Executes a run against the AI provider.

This is the main execution entry point. The adapter should:

  1. Emit a :run_started event
  2. Send the request to the provider
  3. Emit events as responses come in (:message_received, :tool_call_started, etc.)
  4. Emit :run_completed or :run_failed when done
  5. Return the final result

Parameters

  • adapter - The adapter instance
  • run - The run to execute (contains input, session_id, etc.)
  • session - The parent session (contains context, metadata)
  • opts - Execution options:
    • :event_callback - Function called for each event emitted
    • :timeout - Maximum execution time in milliseconds

Returns

  • {:ok, result} - Execution completed successfully
    • result.output - The final output from the provider
    • result.token_usage - Token usage statistics
    • result.events - All events emitted during execution
  • {:error, Error.t()} - Execution failed

Examples

iex> callback = fn event -> Logger.info("Event: #{inspect(event)}") end
iex> MyAdapter.execute(adapter, run, session, event_callback: callback)
{:ok, %{
  output: %{content: "Hello!"},
  token_usage: %{input_tokens: 10, output_tokens: 20},
  events: [...]
}}

name(adapter)

@callback name(adapter()) :: String.t()

Returns the unique name of this provider.

This name is used for logging, metrics, and identifying the provider in multi-provider configurations.

Examples

iex> MyAdapter.name(adapter)
"anthropic"

validate_config(arg1, map)

@callback validate_config(adapter() | module(), map()) ::
  :ok | {:error, AgentSessionManager.Core.Error.t()}

Validates provider-specific configuration.

This is called before the adapter starts to ensure all required configuration is present and valid.

Parameters

  • adapter - The adapter instance (or module for static validation)
  • config - Configuration map to validate

Returns

  • :ok - Configuration is valid
  • {:error, Error.t()} - Configuration is invalid

Examples

iex> MyAdapter.validate_config(adapter, %{api_key: "sk-..."})
:ok

iex> MyAdapter.validate_config(adapter, %{})
{:error, %Error{code: :validation_error, message: "api_key is required"}}

Functions

cancel(adapter, run_id)

@spec cancel(adapter(), String.t()) ::
  {:ok, String.t()} | {:error, AgentSessionManager.Core.Error.t()}

Cancels a run.

capabilities(adapter)

@spec capabilities(adapter()) ::
  {:ok, [AgentSessionManager.Core.Capability.t()]}
  | {:error, AgentSessionManager.Core.Error.t()}

Returns the list of capabilities.

execute(adapter, run, session, opts \\ [])

Executes a run.

name(adapter)

@spec name(adapter()) :: String.t()

Returns the provider name.

resolve_execute_timeout(opts)

@spec resolve_execute_timeout(keyword()) :: pos_integer()

Resolves execute call timeout from options, including a grace period for asynchronous result handoff back to callers.

validate_config(adapter, config)

@spec validate_config(adapter() | module(), map()) ::
  :ok | {:error, AgentSessionManager.Core.Error.t()}

Validates configuration.