PhoenixKitAgent.Config (PhoenixKitAgent v0.1.0)

Copy Markdown View Source

The module settings (phoenix_kit_settings, keys agent_*) and their defaults. A per-user value (PhoenixKitAgent.Schemas.UserSettings) wins over these; these win over the built-in defaults below.

KeyDefault
agent_enabledfalse (see PhoenixKitAgent.enabled?/0)
agent_default_endpoint_uuidnone — the AI endpoint the assistant uses
agent_vision_endpoint_uuidnone — describes images for a model without image input
agent_system_promptnone — the module's system prompt (JSON {"text": …})
agent_daily_cost_limit_micro_usd1_000_000 ($1)
agent_monthly_cost_limit_micro_usd10_000_000 ($10)
agent_daily_turn_limit200
agent_max_attachment_mb20
agent_max_attachments_per_message5
agent_limit_watchersnone — JSON {"uuids": […]} of user uuids; none = every Owner
agent_max_tool_rounds8
agent_turn_deadline_s300
agent_history_chars24_000
agent_tool_result_chars12_000
agent_capture_ai_contentfalse

Money is in micro-dollars ($1 = 1_000_000); the AI journal keeps dollars (phoenix_kit_ai_requests.cost), read through cost_to_micro/1. A stored value that is not a whole number, or is outside its range (below), reads as the default. A limit of 0 is a real limit (nothing allowed); a technical value must be positive.

Core keeps a string setting to 1000 characters, so the two settings that can be longer — the module prompt and the watchers — are JSON settings (value_json), written by put_system_prompt/2 and put_limit_watchers/2. The two models are written by put_default_endpoint/2 and put_vision_endpoint/2, which take only an AI endpoint that chats and fits its AI task (agent.chat, agent.attachment).

Every writer records the change with who made it (actor_uuid: in opts) in the core Activity log: through the settings history (setting.changed, with the values before and after) — except the module prompt, whose text must not be kept there (nor in the query log that history's write goes to): it is written without that history, and its change is recorded as agent.settings_updated with the field name only.

agent_capture_ai_content is on only when it reads exactly "true": then the bodies of the assistant's model calls are written to the AI request journal, where every holder of the ai permission reads them. Only an Owner may switch it: put_capture_ai_content/2 does not check that, its caller (the Settings page, PhoenixKitAgent.Web.SettingsLive) does.

Summary

Functions

Whether the bodies of the assistant's model calls go to the AI journal.

A cost in dollars as the AI journal keeps it (PhoenixKitAI.Request.cost, a Decimal, finer than a micro-dollar at times) as whole micro-dollars, rounded half up: $0.0000025 is 3. nil (no cost known) stays nil. A sum is rounded once, after it is added up, never row by row.

The AI endpoint the assistant uses by default, or nil.

Validates the defaults form; see save_defaults/2.

The current defaults as the form shows them: string values, money in dollars.

How many characters of earlier messages go into the model's context.

The module's default per-user limits.

The users told when someone hits a limit (agent_limit_watchers). Empty means every Owner; who those are is decided where the notice is sent.

Most model ↔ tool rounds in one turn.

Micro-dollars as dollars for a form field: 1_500_000 is "1.5".

Switches agent_capture_ai_content. opts go to PhoenixKit.Settings (actor_uuid:, source:). The caller checks that the person is an Owner.

Sets the AI endpoint the assistant uses by default (agent_default_endpoint_uuid), or none (nil). Only an existing endpoint that chats is taken; anything else is {:error, :invalid_endpoint} and nothing is written. A newly chosen endpoint must fit the agent.chat AI task (PhoenixKitAI.Tasks.check_selection/2): one whose model is known not to take tools is {:error, {:model_unfit, missing}}, one nobody knows takes them {:error, {:model_unverified, unclear}}, and nothing is written; the endpoint already set is not checked again. opts go to PhoenixKit.Settings (actor_uuid:, source:). The caller checks agent.manage.

Sets who is told when someone hits a limit (agent_limit_watchers): user uuids, repeats dropped; an empty list means every Owner. Anything that is not a uuid is {:error, :invalid_uuid} and nothing is written. The caller checks agent.manage.

Sets the module's system prompt (agent_system_prompt); a blank one means none. NUL characters are cut (the database refuses them); a prompt over 16000 characters is {:error, changeset} and nothing is written — it goes into every request. A change is recorded in the Activity log as agent.settings_updated by actor_uuid: (in opts), with the field name and never the text (see the moduledoc). The caller checks agent.manage.

Sets the AI endpoint that describes pictures for a model that does not see them (agent_vision_endpoint_uuid), or none (nil); as put_default_endpoint/2, against the agent.attachment AI task (its model must take pictures).

Takes user_uuid out of agent_limit_watchers, under the setting's row lock (a concurrent change of the list is not lost). {:ok, :unchanged} when it was not in the list.

Saves the defaults form: dollars become micro-dollars; every value is checked against its range and nothing is written unless all are valid. Only values that differ from what is in effect are written, so an untouched built-in default stays unstored (and follows a later change of the default). They are written in one transaction (PhoenixKit.Settings.update_settings_batch/2): a write the database refuses leaves every setting as it was and is {:error, :not_saved}. opts go to PhoenixKit.Settings (actor_uuid:, source:) so the change is in the settings history with who made it.

The module's system prompt, or nil.

How many characters of one tool result go to the model (and are kept).

Deadline of a whole turn, in seconds.

Dollars typed by a person ("2,5", "0.05") as micro-dollars: {:ok, integer}, {:error, :fraction} when finer than a millionth of a dollar, {:error, reason} for anything that is not a non-negative amount.

The AI endpoint that describes images for a model without image input, or nil.

Types

endpoint_refusal()

@type endpoint_refusal() ::
  :invalid_endpoint | {:model_unfit | :model_unverified, [atom()]}

Why put_default_endpoint/2 or put_vision_endpoint/2 wrote nothing.

Functions

capture_ai_content?()

@spec capture_ai_content?() :: boolean()

Whether the bodies of the assistant's model calls go to the AI journal.

cost_to_micro(usd)

@spec cost_to_micro(Decimal.t() | nil) :: non_neg_integer() | nil

A cost in dollars as the AI journal keeps it (PhoenixKitAI.Request.cost, a Decimal, finer than a micro-dollar at times) as whole micro-dollars, rounded half up: $0.0000025 is 3. nil (no cost known) stays nil. A sum is rounded once, after it is added up, never row by row.

default_endpoint_uuid()

@spec default_endpoint_uuid() :: String.t() | nil

The AI endpoint the assistant uses by default, or nil.

defaults_changeset(params)

@spec defaults_changeset(map()) :: Ecto.Changeset.t()

Validates the defaults form; see save_defaults/2.

defaults_form_values()

@spec defaults_form_values() :: %{required(String.t()) => String.t()}

The current defaults as the form shows them: string values, money in dollars.

history_chars()

@spec history_chars() :: pos_integer()

How many characters of earlier messages go into the model's context.

limit_defaults()

@spec limit_defaults() :: %{required(atom()) => non_neg_integer()}

The module's default per-user limits.

limit_watchers()

@spec limit_watchers() :: [String.t()]

The users told when someone hits a limit (agent_limit_watchers). Empty means every Owner; who those are is decided where the notice is sent.

max_tool_rounds()

@spec max_tool_rounds() :: pos_integer()

Most model ↔ tool rounds in one turn.

micro_to_usd(micro)

@spec micro_to_usd(non_neg_integer()) :: String.t()

Micro-dollars as dollars for a form field: 1_500_000 is "1.5".

put_capture_ai_content(value, opts)

@spec put_capture_ai_content(boolean(), keyword()) ::
  {:ok, term()} | {:error, Ecto.Changeset.t()}

Switches agent_capture_ai_content. opts go to PhoenixKit.Settings (actor_uuid:, source:). The caller checks that the person is an Owner.

put_default_endpoint(uuid, opts)

@spec put_default_endpoint(String.t() | nil, keyword()) ::
  {:ok, term()} | {:error, endpoint_refusal() | Ecto.Changeset.t()}

Sets the AI endpoint the assistant uses by default (agent_default_endpoint_uuid), or none (nil). Only an existing endpoint that chats is taken; anything else is {:error, :invalid_endpoint} and nothing is written. A newly chosen endpoint must fit the agent.chat AI task (PhoenixKitAI.Tasks.check_selection/2): one whose model is known not to take tools is {:error, {:model_unfit, missing}}, one nobody knows takes them {:error, {:model_unverified, unclear}}, and nothing is written; the endpoint already set is not checked again. opts go to PhoenixKit.Settings (actor_uuid:, source:). The caller checks agent.manage.

put_limit_watchers(uuids, opts)

@spec put_limit_watchers([String.t()], keyword()) ::
  {:ok, term()} | {:error, :invalid_uuid | Ecto.Changeset.t()}

Sets who is told when someone hits a limit (agent_limit_watchers): user uuids, repeats dropped; an empty list means every Owner. Anything that is not a uuid is {:error, :invalid_uuid} and nothing is written. The caller checks agent.manage.

put_system_prompt(prompt, opts)

@spec put_system_prompt(String.t() | nil, keyword()) ::
  {:ok, term()} | {:error, Ecto.Changeset.t()}

Sets the module's system prompt (agent_system_prompt); a blank one means none. NUL characters are cut (the database refuses them); a prompt over 16000 characters is {:error, changeset} and nothing is written — it goes into every request. A change is recorded in the Activity log as agent.settings_updated by actor_uuid: (in opts), with the field name and never the text (see the moduledoc). The caller checks agent.manage.

put_vision_endpoint(uuid, opts)

@spec put_vision_endpoint(String.t() | nil, keyword()) ::
  {:ok, term()} | {:error, endpoint_refusal() | Ecto.Changeset.t()}

Sets the AI endpoint that describes pictures for a model that does not see them (agent_vision_endpoint_uuid), or none (nil); as put_default_endpoint/2, against the agent.attachment AI task (its model must take pictures).

remove_limit_watcher(user_uuid)

@spec remove_limit_watcher(String.t()) :: {:ok, term()} | {:error, Ecto.Changeset.t()}

Takes user_uuid out of agent_limit_watchers, under the setting's row lock (a concurrent change of the list is not lost). {:ok, :unchanged} when it was not in the list.

save_defaults(params, opts)

@spec save_defaults(map(), keyword()) ::
  {:ok, [String.t()]} | {:error, Ecto.Changeset.t() | :not_saved}

Saves the defaults form: dollars become micro-dollars; every value is checked against its range and nothing is written unless all are valid. Only values that differ from what is in effect are written, so an untouched built-in default stays unstored (and follows a later change of the default). They are written in one transaction (PhoenixKit.Settings.update_settings_batch/2): a write the database refuses leaves every setting as it was and is {:error, :not_saved}. opts go to PhoenixKit.Settings (actor_uuid:, source:) so the change is in the settings history with who made it.

system_prompt()

@spec system_prompt() :: String.t() | nil

The module's system prompt, or nil.

tool_result_chars()

@spec tool_result_chars() :: pos_integer()

How many characters of one tool result go to the model (and are kept).

turn_deadline_s()

@spec turn_deadline_s() :: pos_integer()

Deadline of a whole turn, in seconds.

usd_to_micro(value)

@spec usd_to_micro(term()) :: {:ok, non_neg_integer()} | {:error, atom()}

Dollars typed by a person ("2,5", "0.05") as micro-dollars: {:ok, integer}, {:error, :fraction} when finer than a millionth of a dollar, {:error, reason} for anything that is not a non-negative amount.

vision_endpoint_uuid()

@spec vision_endpoint_uuid() :: String.t() | nil

The AI endpoint that describes images for a model without image input, or nil.