Current section
Files
Jump to
Current section
Files
lib/altar/ai/adapters/mock.ex
defmodule Altar.AI.Adapters.Mock do
@moduledoc """
Mock adapter for testing - configurable responses.
Allows you to configure specific responses for different operations,
making it ideal for testing without calling real AI services.
"""
defstruct [:responses, :call_log, opts: []]
@type t :: %__MODULE__{
responses: map(),
call_log: list(),
opts: keyword()
}
@doc """
Create a new mock adapter.
## Examples
iex> mock = Altar.AI.Adapters.Mock.new()
iex> mock = Altar.AI.Adapters.Mock.with_response(mock, :generate, {:ok, %Altar.AI.Response{content: "test"}})
"""
def new(opts \\ []) do
responses = Keyword.get(opts, :responses, %{})
%__MODULE__{responses: responses, call_log: [], opts: opts}
end
@doc """
Configure a response for a specific operation.
"""
def with_response(mock, operation, response) do
%{mock | responses: Map.put(mock.responses, operation, response)}
end
@doc """
Always available.
"""
def available?, do: true
end
defimpl Altar.AI.Generator, for: Altar.AI.Adapters.Mock do
alias Altar.AI.Response
def generate(%{responses: responses}, prompt, _opts) do
case Map.get(responses, :generate) do
nil ->
{:ok, %Response{content: "Mock response for: #{prompt}", provider: :mock, model: "mock"}}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 1) ->
fun.(prompt)
end
end
def stream(%{responses: responses}, prompt, _opts) do
case Map.get(responses, :stream) do
nil ->
# Return a simple mock stream
{:ok, Stream.map([prompt], fn p -> "Mock stream for: #{p}" end)}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 1) ->
fun.(prompt)
end
end
end
defimpl Altar.AI.Embedder, for: Altar.AI.Adapters.Mock do
def embed(%{responses: responses}, text, _opts) do
case Map.get(responses, :embed) do
nil ->
# Return a mock embedding vector (256 dimensions)
{:ok, Enum.map(1..256, fn _ -> :rand.uniform() end)}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 1) ->
fun.(text)
end
end
def batch_embed(%{responses: responses} = mock, texts, opts) do
case Map.get(responses, :batch_embed) do
nil ->
# Return mock embeddings for each text
{:ok,
Enum.map(texts, fn _ ->
{:ok, vec} = embed(mock, "", opts)
vec
end)}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 1) ->
fun.(texts)
end
end
end
defimpl Altar.AI.Classifier, for: Altar.AI.Adapters.Mock do
alias Altar.AI.Classification
def classify(%{responses: responses}, text, labels, _opts) do
case Map.get(responses, :classify) do
nil ->
# Simple mock: pick first label with high confidence
{:ok, Classification.new(List.first(labels), 0.95, %{List.first(labels) => 0.95})}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 2) ->
fun.(text, labels)
end
end
end
defimpl Altar.AI.CodeGenerator, for: Altar.AI.Adapters.Mock do
alias Altar.AI.CodeResult
def generate_code(%{responses: responses}, prompt, _opts) do
case Map.get(responses, :generate_code) do
nil ->
{:ok, %CodeResult{code: "# Mock code for: #{prompt}", language: "elixir"}}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 1) ->
fun.(prompt)
end
end
def explain_code(%{responses: responses}, code, _opts) do
case Map.get(responses, :explain_code) do
nil ->
{:ok, "Mock explanation for code: #{String.slice(code, 0, 50)}..."}
{:ok, _} = resp ->
resp
{:error, _} = err ->
err
fun when is_function(fun, 1) ->
fun.(code)
end
end
end