Packages
fnord
0.8.99
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/agent.ex
defmodule AI.Agent do
@moduledoc """
Behavior for AI agents that process instructions and return responses.
This behavior defines the contract between the coordinator and specialized
agents, ensuring consistent interfaces and proper error handling across the
agent system.
"""
# ----------------------------------------------------------------------------
# Behavior Callbacks
# ----------------------------------------------------------------------------
@callback get_response(map) :: {:ok, any()} | {:error, any()}
# ----------------------------------------------------------------------------
# Public API
# ----------------------------------------------------------------------------
defstruct [
:name,
:named?,
:impl
]
@type t :: %__MODULE__{
name: nil | binary,
named?: boolean,
impl: module
}
@doc """
Create a new agent instance.
If `:named?` is set to `false`, the agent will not be assigned a name. This
is intended specifically for `AI.Agent.Nomenclater`, which does the naming on
behalf of `Services.NamePool`, which can't be used directly by `Nomenclater`
because that would create a circular dependency.
"""
@spec new(module, keyword) :: t
def new(impl, opts \\ []) do
with :ok <- validate_agent_module(impl) do
%__MODULE__{
name: Keyword.get(opts, :name, nil),
named?: Keyword.get(opts, :named?, true),
impl: impl
}
else
{:error, reason} ->
raise "Failed to create agent: #{inspect(reason)}"
end
end
@doc """
Delegate to the agent implementation's `get_response/1` function. Includes
the agent in the args map.
The agent's name is managed here, checking out a name from
`Services.NamePool` if the agent is named but doesn't yet have a name, or
associating the agent's existing name if it does. The name is checked back in
after the response is generated.
The call is wrapped in a `Task` to provide a global identifier for logging
and tracing purposes, which is associated with the agent's name.
"""
@spec get_response(t, map) :: {:ok, any} | {:error, any}
def get_response(agent, args) do
parent_pool = HttpPool.get()
Services.Globals.Spawn.async(fn ->
HttpPool.set(parent_pool)
agent =
cond do
agent.named? && is_binary(agent.name) ->
Services.NamePool.associate_name(agent.name)
agent
agent.named? ->
Services.NamePool.checkout_name()
|> case do
{:ok, name} -> %{agent | name: name}
{:error, _} -> %{agent | name: Services.NamePool.default_name()}
end
true ->
name = Services.NamePool.default_name()
%{agent | name: name}
end
try do
args
|> Map.put(:agent, agent)
|> agent.impl.get_response()
after
if agent.named? && is_binary(agent.name) do
Services.NamePool.checkin_name(agent.name)
end
end
end)
|> Task.await(:infinity)
end
@doc """
Delegate to `AI.Completion.get/1` with the agent's name included in the args.
Intended to be called by implementors of `AI.Agent` when they need to
generate completions as part of their response processing.
"""
@spec get_completion(t, keyword) :: {:ok, AI.Completion.t()} | {:error, any}
def get_completion(agent, args) do
args
|> Keyword.put(:name, agent.name)
|> AI.Completion.get()
end
@doc """
Delegate to `AI.Completion.tools_used/1` to extract the tools used from a
completion.
"""
@spec tools_used(AI.Completion.t()) :: %{binary => non_neg_integer()}
def tools_used(completion) do
AI.Completion.tools_used(completion)
end
# ----------------------------------------------------------------------------
# Private functions
# ----------------------------------------------------------------------------
defp validate_agent_module(mod) do
cond do
!Code.ensure_loaded?(mod) -> {:error, :module_not_found}
!function_exported?(mod, :__info__, 1) -> {:error, :not_a_module}
true -> :ok
end
end
end