Skip to content

Service Callers ​

An application that calls UGENT for a service rather than for a person — classifying customer comments, tagging tickets — usually wants the same things on every turn: no code context, no memory, a model that answers quickly, and its transcripts deleted once the work is done. Per-caller defaults set those for every turn from one caller, with no change on the caller's side; What Is Kept lists what a deletion leaves.

A caller is known by the federation issuer its requests assert: federation_issuer on the web channel API. A request with no federation claim, or a blank one, gets no defaults.

Document Author: Uni Zhu

Per-Caller Defaults ​

toml
[caller_defaults."example-issuer"]
disable_code_context  = true   # skip the pre-LLM code-context bundle
disable_memory_recall = true   # recall no workspace memory into the turn
disable_memory_write  = true   # no write-back, no compaction summary, no memory_control
model = "dashscope:dashscope-nothink@qwen3.8-flash"   # optional
KeyDefaultEffect
disable_code_contextfalseSkip the pre-LLM code-context bundle. The search never starts, so the turn's text is not sent to the context engine for it. Tools the model calls itself are unaffected.
disable_memory_recallfalseRecall no workspace memory into the turn.
disable_memory_writefalseKeep the turn out of workspace memory: no post-turn extraction, no compaction summary, and no memory_control tool.
modelunsetThe model these turns run on when the request names none and the caller has no stored /model choice.
transcript_retentionunsetDelete these turns' transcripts once unused. See Transcript Retention.

How the defaults apply:

  • They only narrow. Each flag means what the request's opt-out of the same name means, and the two combine by OR: a request cannot switch back on what a default switched off, and can still switch off what the defaults leave on. The per-request opt-outs are part of the web channel API.
  • The issuer is matched exactly, after trimming, as the request asserts it.
  • model is validated like a request's. It takes a bare model name or <provider>:<instance>@<model>, and must name an instance with allow_user_select = true (see Per-User Model Selection). One that stops validating is logged as a warning, and the turn runs on the global default.
  • An unknown key fails the config load. A misspelt key would otherwise parse, apply nothing, and look as if it worked.
  • Delegated work keeps them. Every task a restricted turn creates — through delegate_task, a follow-up or a plan revision — carries the turn's opt-outs and tool lists to its worker. A worker of a turn that kept out of memory inherits none, whatever subagent_inheritance says. A restricted task never runs on a swarm peer: one left to automatic placement runs on this node, and one sent to a peer explicitly is refused with a message to delegate it locally. A restricted turn is not offered cron_management, because a scheduled job runs as the owner with none of the restrictions.

The issuer is a claim

A request names its own issuer, so per-caller defaults are a preference layer, not a security boundary. That is enough for what they hold: every flag only narrows, and the model must already be user-selectable, so a caller that names another caller's issuer changes only its own turns. A transcript retention likewise purges only a conversation every turn of which came from the one actor that claimed it.

A Caller That Should Not Reason ​

Reasoning is configured per LLM instance, so a caller gets a different reasoning profile by running on a copy of the instance. For DashScope, mode = "disabled" alone only omits enable_thinking and leaves the model on its own default, so turn thinking off explicitly:

toml
[llm.instances.dashscope-nothink]
type = "dashscope"
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
api_key_ref = "@dashscope_api_key"
default_model = "qwen3.8-flash"
allow_user_select = true
models_allowlist = ["qwen3.8-flash"]

[llm.instances.dashscope-nothink.reasoning]
mode = "disabled"

[llm.instances.dashscope-nothink.reasoning.provider.openai_compatible]
extra_body = { enable_thinking = false }

Keep the main instance allow_user_select = true as well. A bare model name that two selectable instances share is served by the default instance when it is one of them; if the default instance is not selectable, the copy captures every bare-name request for that model.

Transcript Retention ​

By default UGENT keeps every turn's transcript, in stores the caller cannot reach. A caller whose service turns carry data it has promised to delete can have those transcripts deleted once they are unused:

toml
[caller_defaults."example-issuer".transcript_retention]
minutes = 60                               # 15 to 41,760 (29 days)
subject_prefixes = ["system:classifier@"]  # matched against the asserted subject
KeyEffect
minutesHow long a conversation waits, unused, before it is purged: 15 to 41,760 (29 days).
subject_prefixesThe subjects covered: a turn whose asserted federation_subject starts with one of these.

The load refuses a window under 15 minutes (a turn's stream can run 900 seconds, and the window counts from the turn's end) or over 29 days (a day inside the 30 days UGENT remembers a turn that never answered), a list that names no prefix, and a prefix that is blank or has surrounding spaces. An unknown key fails the load too.

Which Turns It Covers ​

  • Named service subjects. A prefix is plain text matched exactly against the subject the request asserts: case matters, and _ and % are ordinary characters. Name only service subjects whose conversations never continue; a person's conversation would lose its history.
  • Turns without tools. A turn is covered only when the request itself denies every tool (ugent_denied_tools: ["*"]). A turn with tools can leave work running after its answer — a delegated worker, a question — that a purge would cut off. So a named subject's turn that may use tools keeps its transcript, and the log says a transcript retention names this subject, but its turn may use tools, so its transcript is kept.
  • One actor's conversations. A conversation is purged only if every turn on it came from the actor whose claim recorded it, so a claim cannot reach anyone else's conversation. A message whose retention cannot be recorded is refused before anything is kept.

What Is Deleted ​

A sweep runs every minute. Once a conversation's last turn ended minutes ago with nothing left to finish — no turn accepted or running, no question waiting, no answer still to deliver — the sweep checks the configuration again. If the conversation is still covered with that window, one transaction deletes each message and its answer, the conversation's event log, its delivered answers and dead letters, its questions and its session map. Each of its sessions is then deleted for good, with the workers it spawned: messages, steering, compression records, verification ledger and caches. A session that cannot be deleted stays queued for the next pass.

  • Turns that never answered. A turn that is stopped — a caller giving up on a long turn — or that fails has still opened a session holding its prompt. That session is recorded with the turn as it ends, and deleted with the conversation.
  • A changed setting reaches recorded conversations. A conversation the retention no longer names is released and kept like any other. A longer window is the one it waits out; a shorter one applies from its next message.
  • Overwritten, not only deleted. Both stores overwrite the space a delete frees within its page, and a purge also overwrites the pages it frees. A page an earlier rewrite freed — a long message's overflow — can keep text until it is reused, and the write-ahead log keeps earlier page images until it is reused.
  • The owner stays. The conversation keeps its recorded owner, an actor id, so it remains closed to everyone else: the owner's next message on that conversation id starts a new session, and anyone else is refused.

What Is Kept ​

  • Message ids. A finished message sent again is answered as already processed and is never run again. One that never ran (abandoned or refused) can still run.
  • Memory a turn wrote. Set disable_memory_write as well.
  • Files uploaded with a message.
  • Checkpoints. With [agent] auto_checkpoint on, as it is by default, a turn's checkpoint and the first line of its prompt, up to 160 characters.
  • Caches and logs. The web channel's in-memory caches, UCTR when it is enabled, and logs.
  • A session interrupted by a restart. If the daemon restarts in the middle of a turn that then never answers, that turn's session is kept.

Goals and harness refinements are left alone; a turn without tools creates neither.

Retention for Every Conversation ​

Two limits apply to every caller, with or without a retention:

  • Stream events older than 30 days are deleted, oldest first, alongside the cap of 50,000 rows.
  • [session] retention_days (default 30) is the window a session_cleanup cron job uses when the job names none. Conversation sessions are cleaned only where such a job is declared, and 0 turns that cleanup off. Finished worker sessions are deleted separately, 7 days after they were last addressed ([session] worker_session_ttl_days).

See Also ​

Released under the Private Beta License.