Packages

Elixir port of Google's Agent Development Kit (ADK) — agent orchestration, sessions, tools, LLM abstraction, plugins, and telemetry. Transport-agnostic.

Current section

Files

Jump to
adk_ex lib adk model mock.ex
Raw

lib/adk/model/mock.ex

defmodule ADK.Model.Mock do
@moduledoc """
Mock model for testing without real API calls.
Configured with a list of responses (either `LlmResponse` structs or
functions that take an `LlmRequest` and return an `LlmResponse`).
Returns them in sequence via an internal Agent process for statefulness.
Falls back to a default text response if the list is exhausted.
"""
@behaviour ADK.Model
alias ADK.Model.{LlmRequest, LlmResponse}
alias ADK.Types.Content
@type response_fn :: (LlmRequest.t() -> LlmResponse.t())
@type response_entry :: LlmResponse.t() | response_fn()
@type t :: %__MODULE__{
model_name: String.t(),
responses: [response_entry()],
pid: pid() | nil
}
@enforce_keys []
defstruct model_name: "mock-model",
responses: [],
pid: nil
@doc """
Creates a new Mock model with an internal Agent for stateful response tracking.
"""
@spec new(keyword()) :: t()
def new(opts \\ []) do
responses = Keyword.get(opts, :responses, [])
model_name = Keyword.get(opts, :model_name, "mock-model")
{:ok, pid} = Agent.start_link(fn -> responses end)
%__MODULE__{model_name: model_name, responses: responses, pid: pid}
end
@impl ADK.Model
def name(%__MODULE__{model_name: name}), do: name
@impl ADK.Model
def generate_content(%__MODULE__{pid: pid} = _model, %LlmRequest{} = request, _stream)
when is_pid(pid) do
response =
Agent.get_and_update(pid, fn
[] ->
default = %LlmResponse{
content: Content.new_from_text("model", "Mock response"),
turn_complete: true
}
{default, []}
[head | tail] ->
resp =
case head do
%LlmResponse{} -> head
fun when is_function(fun, 1) -> fun.(request)
end
{resp, tail}
end)
[response]
end
def generate_content(%__MODULE__{responses: responses} = _model, %LlmRequest{} = request, _stream) do
# Fallback for struct-based mock without pid (backward compat)
case responses do
[] ->
[%LlmResponse{content: Content.new_from_text("model", "Mock response"), turn_complete: true}]
[head | _] ->
resp =
case head do
%LlmResponse{} -> head
fun when is_function(fun, 1) -> fun.(request)
end
[resp]
end
end
@doc """
Pops the next response from the model, returning {response, updated_model}.
Useful for tests that need to track consumed responses.
"""
@spec pop_response(t(), LlmRequest.t()) :: {LlmResponse.t(), t()}
def pop_response(%__MODULE__{responses: []} = model, _request) do
response = %LlmResponse{
content: Content.new_from_text("model", "Mock response"),
turn_complete: true
}
{response, model}
end
def pop_response(%__MODULE__{responses: [head | tail]} = model, request) do
response =
case head do
%LlmResponse{} -> head
fun when is_function(fun, 1) -> fun.(request)
end
{response, %{model | responses: tail}}
end
end