Light Dark

Functions

AgentRuntime alias

Alias of ::rt/AgentRuntime

ChatOptions alias

Alias of ::ai-chat/ChatOptions

Identity alias

Alias of ::session/Identity

ReplyDelta alias

Alias of ::ai-chat/ReplyDelta

Session alias

Alias of ::session/Session

bool-default

fn (value: Bool?, fallback: Bool): Bool

Treat null as "unset" and return fallback; otherwise return value as-is. Avoids or(false, true) returning trueor treats false as falsy in Hot, so it's the wrong primitive for boolean opts with true defaults.

build-system

fn (cfg: TurnConfig, input: TurnInput): Str

Compose the system prompt for this turn.

Calls ::ai::rag/build-context-safe against memory as it stood before the current turn is persisted, then appends a heading + context block to cfg.system-base via ::ai::rag/with-memory-block.

now-secs

fn (): Int

ns alias

Alias of ::ai::agent::chat-turn/

One-call wrapper around the "memory-grounded LLM reply" lifecycle.

Every memory-keyed chat agent we've written (PersonalAgent, TeamAgent, …) reimplements the same five-step turn:

  1. Build a memory-grounded system prompt (RAG over prior state).
  2. Persist the new user turn to session memory.
  3. Bind the per-request session/identity into ctx so tools recover them safely.
  4. Stream the LLM response token-by-token, forwarding TextDeltas to the agent's reply stream so clients render live.
  5. (Optionally) persist the assistant turn so the next call has it as context.

Step ordering is load-bearing. A common bug — call it "RAG self poisoning" — is doing step 2 before step 1, which makes the user's fresh message land in their own retrieval results and convinces the model that everything they say is already in memory.

run-chat-turn enforces the correct order. Concrete agents pass config + per-turn input; the helper returns {ok, text} ready to be used as the event-handler return value.

Offline / no-key handling

Callers can set fallback-text to short-circuit the LLM call and emit a deterministic reply instead. Use this when probing for a provider key and the probe came back empty (e.g. is-empty(::hot::ctx/get('anthropic.api.key', ''))). The library intentionally does not own the key probe — that's provider-specific and likely to live in ::ai::chat/provider-available? in a future iteration.

persist-assistant-turn

fn (cfg: TurnConfig, input: TurnInput, text: Str): Null

Persist an assistant turn so future RAG retrievals can see it.

The record's metadata always carries role: "assistant". Skipped silently when text is empty.

persist-user-turn

fn (cfg: TurnConfig, input: TurnInput): Null

Persist a user turn into the session-keyed agent memory.

The record's metadata always carries role: "user", even when the caller supplies user-metadata. This keeps the role tag symmetric with assistant records and lets synthesis handlers filter by role via ::ai::rag/recent-text (include-roles / exclude-roles).

Returns null. Errors during persistence are swallowed so a transient store failure doesn't kill the reply.

reply-payload

fn (input: TurnInput): Map

Normalize {session_id, message_id} for stream emits.

run-chat-turn

fn (cfg: TurnConfig, input: TurnInput): Map

Execute one memory-grounded chat turn.

Enforces ordering:

  1. Build the system prompt from prior memory (no self-poisoning).
  2. Persist the user turn (default; opt out via persist-user).
  3. Bind the per-request ctx for tools (default; opt out via bind-tool-ctx).
  4. Stream the LLM reply OR emit fallback-text if non-null.
  5. Persist the assistant turn (default; opt out via persist-assistant).

Returns {ok: true, text: <final-text>}. The handler can return this directly.

stream-llm

fn (cfg: TurnConfig, payload: Map, system: Str, prompt: Str): Str

Run the configured LLM with token-level streaming forwarded to the agent reply stream. Internal helper — run-chat-turn orchestrates.

user-body

fn (input: TurnInput): Str

Compose the text we persist for a user turn (raw text + attachment summary).

user-prompt

fn (input: TurnInput): Str

Compose the user prompt sent to the LLM (raw text + attachment block).

Types

TurnConfig

TurnConfig type {
    rt: ::ai::agent::runtime/AgentRuntime,
    agent-name: Str,
    stream-label: Str,
    chat-opts: ::ai::chat/ChatOptions,
    system-base: Str
}

Per-handler configuration: identity + memory scope + LLM transport.

Shared across every turn the handler serves. Build it once near the top of the handler and reuse for each call.

TurnInput

TurnInput type {
    session: ::ai::session/Session,
    sender: ::ai::session/Identity,
    text: Str,
    attachments: Vec?,
    payload: Map?,
    recall-opts: Map?,
    memory-heading: Str?,
    persist-user: Bool?,
    persist-assistant: Bool?,
    bind-tool-ctx: Bool?,
    ctx-extras: Map?,
    user-source: Str?,
    user-metadata: Map?,
    assistant-source: Str?,
    assistant-metadata: Map?,
    fallback-text: Str?,
    system-override: Str?
}

Per-turn input: who's talking, what they said, optional knobs.

  • session / sender — resolved by the handler from the event.
  • text — the user's message, raw.
  • attachmentsVec<Attachment>?, summarized into the persisted body and woven into the user prompt.
  • payload{session_id?, message_id?}, forwarded onto every reply:start|delta|end event so the UI can correlate.
  • recall-opts — passed to ::ai::rag/build-context-safe. Defaults to {limit: 5, min-score: 0.3}.
  • memory-heading — heading text for the appended memory block. Defaults to "## Memory".
  • persist-user / persist-assistant — default true. Set to false for dry runs or alternative storage strategies.
  • bind-tool-ctx — default true. When set, calls ::ai::agent::request/bind(session, sender, ctx-extras) so LLM tools registered at namespace top-level can recover the caller via ::ai::agent::request/session / ::ai::agent::request/sender.
  • ctx-extras — additional fields stored under ai.agent.extras.
  • user-source / user-metadata — source field + metadata map for the persisted user record. Default source is cfg.agent-name.
  • assistant-source / assistant-metadata — same for the assistant record.
  • fallback-text — when non-null, the LLM is skipped and this string is emitted as the final reply (and optionally persisted). Use for offline fallbacks when no provider key is wired.
  • system-override — when non-null, replaces the system prompt the orchestrator would otherwise build from cfg.system-base + RAG. Use for synthesis-style handlers (e.g. /summary, /tasks) that hand the LLM a pre-baked prompt over a recency window rather than memory-grounded free chat.