The assistant settings of one user (spec §5.4, §7) and how they combine with the module's.
What is in effect
effective/1 is the one place the turn loop reads a user's settings: the
user's own value (PhoenixKitAgent.Schemas.UserSettings), else the module
default (PhoenixKitAgent.Config). A nil field means "the default"; 0 is
a real limit. A blank prompt is no prompt. The model (endpoint_uuid) is read
when a turn starts, so a change takes effect with the next turn of any chat
(spec §14, В6).
enabled?/1 is the user's own switch: false only when an admin switched the
assistant off for them (it never grants the agent permission).
Who writes what
- The user —
put_personal_instructions/3andget_personal_instructions/2, for their ownpersonal_instructionsonly. LikePhoenixKitAgent.Conversationsthey act for a live browser session and needimpersonated: booleanin the options; an impersonated session, a revoked one, a user withoutagentor one switched off is refused. The write replaces that one column, so nothing an admin set can change through it. - An admin —
admin_update/4, with the scope of a person who holdsagent.manage(checked again here, so a caller that forgets is refused). It writes the switch, the model, the admin's prompt, the limits and the notice flags (UserSettings.admin_changeset/2) and records who changed which fields for whom in the core Activity log — field names only, never a prompt text. An Owner's settings are changed by an Owner only, and a form opened before someone else's save does not overwrite it (expected:).
Summary
Types
The settings in force for a user.
Functions
Saves what an admin sets for target_user_uuid (attrs as
UserSettings.admin_changeset/2 casts them) on behalf of actor_scope,
which must hold agent.manage — and be an Owner when the target is one: an
Owner's assistant is not set by the people an Owner manages. The row is
created if the user has none, and locked while it is changed.
Whether user_uuid is one of agent_users/0.
The people the assistant is for: active users who hold agent through a role
(and the Owners, who hold every permission), by e-mail. The admin screen lists
these.
The settings in force for user_uuid: their own value, else the module
default (see the moduledoc). own lists the fields (model, prompt, limits)
the user's row sets itself.
effective/1 for several users with one query and one read of the module
defaults: %{user_uuid => effective}.
Whether the assistant is not switched off for the user (see the moduledoc).
The endpoints the assistant's turns use now: the module default and
every user's own model. It backs the endpoints of the agent.chat AI
task (PhoenixKitAgent.ai_tasks/0).
The user's settings row as it is stored, for the admin's form: an unsaved
%UserSettings{} when they have none. nil for a malformed uuid.
The session user's own personal instructions (nil when none). Refusals are
those of PhoenixKitAgent.Conversations.
Whether actor_scope may set the assistant of target_user_uuid with
admin_update/4: it holds agent.manage, and is an Owner if the target is.
Sets the session user's own personal instructions (a blank text clears them).
They are the user's, ranked below the module's and the admin's prompt (spec
§7.1). Only this one column is written, whatever else the row holds; a text
over 4000 characters, or one that is not text, is {:error, changeset} and
nothing is written.
The people who may be chosen to be told when someone reaches a limit
(agent_limit_watchers, spec §7.4): active users who hold agent.manage
through a role, the Owners, and whoever is a watcher now, by e-mail.
Types
@type effective() :: %{ user_uuid: String.t(), enabled?: boolean(), endpoint_uuid: String.t() | nil, system_prompt: String.t() | nil, personal_instructions: String.t() | nil, daily_cost_limit_micro_usd: non_neg_integer(), monthly_cost_limit_micro_usd: non_neg_integer(), daily_turn_limit: non_neg_integer(), max_attachment_mb: non_neg_integer(), max_attachments_per_message: non_neg_integer(), notify_on_limit: boolean(), own: [atom()] }
The settings in force for a user.
Functions
@spec admin_update(PhoenixKit.Users.Auth.Scope.t() | nil, term(), map(), keyword()) :: {:ok, PhoenixKitAgent.Schemas.UserSettings.t(), [atom()]} | {:error, :forbidden | :not_found | :stale | {:model_unfit | :model_unverified, [atom()]} | Ecto.Changeset.t()}
Saves what an admin sets for target_user_uuid (attrs as
UserSettings.admin_changeset/2 casts them) on behalf of actor_scope,
which must hold agent.manage — and be an Owner when the target is one: an
Owner's assistant is not set by the people an Owner manages. The row is
created if the user has none, and locked while it is changed.
expected: %{field => value} in opts is what the caller's form showed
when it was opened: when the stored row no longer holds those values (an
admin saved in between), nothing is written and the answer is {:error, :stale}, so a stale form never overwrites that change unseen. Fields not
named there do not count.
Returns {:ok, settings, changed_fields}; when fields changed, the actor,
the user (as the resource of the entry) and the field names go to the core
Activity log (action agent.user_settings_updated) — no values, and no
notification for the user. {:error, :forbidden} without the permission
(or for an Owner's settings, to a non-Owner), {:error, :not_found} for an
unknown user, {:error, changeset} for an invalid value (nothing is
written). A newly chosen model must fit the agent.chat AI task
(PhoenixKitAI.Tasks.check_selection/2): one known not to take tools is
{:error, {:model_unfit, missing}}, one nobody knows takes them {:error, {:model_unverified, unclear}}, and nothing is written; none (the module
default) and the model already saved are not checked.
Whether user_uuid is one of agent_users/0.
@spec agent_users() :: [PhoenixKit.Users.Auth.User.t()]
The people the assistant is for: active users who hold agent through a role
(and the Owners, who hold every permission), by e-mail. The admin screen lists
these.
The settings in force for user_uuid: their own value, else the module
default (see the moduledoc). own lists the fields (model, prompt, limits)
the user's row sets itself.
effective/1 for several users with one query and one read of the module
defaults: %{user_uuid => effective}.
Whether the assistant is not switched off for the user (see the moduledoc).
@spec endpoint_uuids() :: [String.t()]
The endpoints the assistant's turns use now: the module default and
every user's own model. It backs the endpoints of the agent.chat AI
task (PhoenixKitAgent.ai_tasks/0).
@spec get(term()) :: PhoenixKitAgent.Schemas.UserSettings.t() | nil
The user's settings row as it is stored, for the admin's form: an unsaved
%UserSettings{} when they have none. nil for a malformed uuid.
@spec get_personal_instructions( PhoenixKitAgent.Conversations.session_ref(), keyword() ) :: {:ok, String.t() | nil} | {:error, PhoenixKitAgent.Conversations.refusal()}
The session user's own personal instructions (nil when none). Refusals are
those of PhoenixKitAgent.Conversations.
@spec may_configure?(PhoenixKit.Users.Auth.Scope.t() | nil, term()) :: boolean()
Whether actor_scope may set the assistant of target_user_uuid with
admin_update/4: it holds agent.manage, and is an Owner if the target is.
@spec put_personal_instructions( PhoenixKitAgent.Conversations.session_ref(), term(), keyword() ) :: {:ok, String.t() | nil} | {:error, PhoenixKitAgent.Conversations.refusal() | Ecto.Changeset.t()}
Sets the session user's own personal instructions (a blank text clears them).
They are the user's, ranked below the module's and the admin's prompt (spec
§7.1). Only this one column is written, whatever else the row holds; a text
over 4000 characters, or one that is not text, is {:error, changeset} and
nothing is written.
@spec watcher_candidates() :: [PhoenixKit.Users.Auth.User.t()]
The people who may be chosen to be told when someone reaches a limit
(agent_limit_watchers, spec §7.4): active users who hold agent.manage
through a role, the Owners, and whoever is a watcher now, by e-mail.