Current section
Files
Jump to
Current section
Files
lib/agens.ex
# Copyright 2024 Jesse Drelick
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
defmodule Agens do
@moduledoc """
Agens is used to create multi-agent workflows with language models.
It is made up of the following core entities:
- `Agens.Serving` - used to interact with language models
- `Agens.Job` - used to define multi-agent workflows
- `Agens.Router` - used to map language model outputs to route instructions
- `Agens.Message` - used to facilitate communication between Jobs and Servings
"""
use DynamicSupervisor
alias Agens.{Job, Serving}
@typedoc "Summary of a supervised Agens child process."
@type process_info :: %{
pid: pid(),
type: :job | :serving,
name: binary(),
config: Job.Config.t() | Serving.Config.t()
}
@doc false
@spec child_spec(keyword()) :: Supervisor.child_spec()
def child_spec(_args) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [[]]},
type: :supervisor
}
end
@doc false
@spec start_link(keyword()) :: Supervisor.on_start()
def start_link(_args) do
DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
@doc false
@impl true
@spec init(:ok) :: {:ok, DynamicSupervisor.sup_flags()}
def init(:ok) do
DynamicSupervisor.init(strategy: :one_for_one)
end
@doc false
@spec serving_pid(atom(), {:error, term()}, (pid() -> any())) :: any()
def serving_pid(name, err, cb) do
case Process.whereis(name) do
nil -> err
pid when is_pid(pid) -> cb.(pid)
end
end
@doc false
@spec job_pid(binary(), {:error, term()}, (pid() -> any())) :: any()
def job_pid(job_id, err, cb) do
case Registry.lookup(Agens.Registry, job_id) do
[] -> err
[{pid, _}] when is_pid(pid) -> cb.(pid)
end
end
@doc """
Generates a random hex-encoded identifier for use as a run, thread, or message id.
"""
@spec generate_uid() :: binary()
def generate_uid do
16
|> :crypto.strong_rand_bytes()
|> Base.encode16(case: :lower)
end
@doc """
Returns a list of all supervised Agens child processes (Jobs and Servings)
with their `pid`, `type`, `name`, and configuration.
"""
@spec active_processes() :: [process_info()]
def active_processes() do
Agens
|> DynamicSupervisor.which_children()
|> Enum.map(fn {_id, pid, _type, [mod]} ->
get_process_info(pid, mod)
end)
end
@doc """
Returns process information for a supervised Agens child given its `pid` and module.
When the module is `Agens.Job`, the result describes a Job. Otherwise it is treated as a Serving.
"""
@spec get_process_info(pid(), module()) :: process_info()
def get_process_info(pid, Job) do
{:ok, config} = Job.get_config(pid)
%{pid: pid, type: :job, name: config.id, config: config}
end
def get_process_info(pid, _) do
{:ok, config} = Serving.get_config(pid)
%{pid: pid, type: :serving, name: Atom.to_string(config.name), config: config}
end
@doc """
Returns the configured list of `Agens.Backend` implementations.
Reads `:agens` application env key `:backends`, defaulting to
`[Agens.Backend.Emit, Agens.Backend.Log]`.
"""
@spec backends() :: [module()]
def backends() do
Application.get_env(:agens, :backends, [Agens.Backend.Emit, Agens.Backend.Log])
end
@doc false
@spec backends(atom(), list()) :: list()
def backends(fun, args) when is_atom(fun) and is_list(args) do
backends()
|> Enum.map(&apply(&1, fun, args))
end
end