LLM provider behavior configuration¶
Provider selection, usage limits, retries, and execution budgets.
llm_provider¶
Configures which LLM backend sase uses and how size aliases map to concrete models. See Providers and models for provider behavior and LLM invocation internals for the processing pipeline.
llm_provider:
provider: claude # or "codex", "qwen", "opencode", "agy", "muse", "grok", "fakey" (default: auto-detect)
# Scalar launch settings. Same grammar as %model; may reference a built-in
# size alias.
default_model: "@large" # used when a launch has no %model directive
epic_lander_model: "@large" # epic land agents below bead.big_epic_phase_threshold
big_epic_lander_model: "@xlarge" # epic land agents at/above that threshold
model_alias_history_limit: 10 # runs shown per alias in Launch Control history
# Override examples; shipped size-alias defaults are generated in the provider reference.
model_aliases:
builtin:
medium: codex/gpt-6.1-sol # specialize the medium size alias
large: claude/opus | codex/gpt-6.1-sol # custom large-phase pool
custom:
blogger:
model: claude/opus
description: Agents that draft and edit blog posts.
bucket: writing # optional Launch Control grouping
buckets:
writing:
description: Writing and editing roles.
Generated reference for llm_provider
Model aliases are resolved when an agent launches, so reusable macros can point at names
such as %model:@medium or %model:@blogger while each user's sase.yml controls the
concrete provider/model. Alias config keys stay bare; the @ marker is only used in
%model/%m directive values. Alias values may reference another alias with
@<alias>; the reference may carry a trailing effort such as @medium@high, which
overrides the referenced alias's effort (chains are followed with cycle/depth
protection). Unknown non-alias model values keep the existing fallback behavior and run
on the default provider. Use model_aliases.builtin to override one of the five
built-in size aliases and model_aliases.custom for user-defined aliases with
descriptions. A | B round-robins across real launches, skips providers whose CLI is
unavailable, and stores its machine-global cursor in ~/.sase/llm_lb.json; display and
preview surfaces only peek. A || B always selects the first installed provider CLI
that is not hard-disabled (a soft-disabled first candidate still wins) and never
reads or advances that cursor. (A | B) || C load-balances the parenthesized pool and
uses the || tail only when every pool member is unavailable (CLI missing or
hard-disabled); an all-soft pool still rotates and does not divert, and tail
selection does not consume the pool cursor. Unparenthesized mixing is still rejected.
Ordered fallback is based on CLI installation plus temporary provider-disable state, not
later model/runtime success, and preserves its first candidate for normal diagnostics
when none are available. Members may carry a trailing effort. Selectors cannot be
nested, and selectors are not accepted in %model directives or launch-scoped/temporary
overrides. In sase's TUI Launch Control, the pool row reports the available/total count,
selector member lists mark the current selection with →, and active temporary
overrides label selection suspended unless their provider is hard-disabled; then the
override is paused and the underlying alias resolves. A soft disable does not pause
the override.
On top of any configured aliases, SASE ships a fixed set of built-in size aliases
that resolve even when unset: @xsmall, @small, @medium, @large, and @xlarge.
Each is a direct selector with no fallback chain to another alias — override one by
setting model_aliases.builtin.<size> to a concrete model, an A | B round-robin pool,
an A || B ordered fallback, or a parenthesized (A | B) || C last-resort. Phase and
task launches route directly to the size alias matching their size metadata; a phase or
task with no size metadata routes through @small. New tasks require an explicit size
after /sase_new_task has ruled out a semantic duplicate and a causally related
in-progress epic. See
Built-in size aliases for the full
shipped-defaults table and
Role Aliases for Delegated Work
for how delegated launches pick a model.
Three scalar llm_provider fields choose the model for launches that aren't driven by
phase/task/tale size routing: default_model (used when a launch has no explicit
%model directive), epic_lander_model (used by epic land agents below
bead.big_epic_phase_threshold authored phases), and big_epic_lander_model (used by
epic land agents at or above that threshold). Each accepts the same grammar as an alias
target — a concrete model, a provider-qualified model, an @alias reference (optionally
with a trailing effort such as @large@high), an A | B pool, an A || B fallback, or
a parenthesized (A | B) || C last-resort. Precedence is unchanged from before the
migration: an explicit prompt/plan/phase/task/approval-picker %model wins first, then
an active temporary override of the selected setting, then the config field resolves
through the normal alias/effort/selector/provider-disable machinery, and a missing or
malformed field falls back to its shipped default (@large / @large / @xlarge).
model_alias_history_limit bounds the number of prior runs requested for each alias in
Launch Control's agent-history panel. It defaults to 10, must be at least 1, and
falls back to 10 at runtime when a malformed value bypasses schema validation. From
that initial window, the panel's Ctrl+J / Ctrl+K keys add or subtract a fixed 10-run
step and never drop below this configured limit; they do not use ace.page_size.
Accepted tale follow-ups without an approval-time model validate the actual handoff plan
and choose the matching size alias directly. Tale plans without size metadata use
@medium. An approval-time model, a %model directive in a custom coder prompt, or an
outer effort suffix remains authoritative.
There is no built-in epic_creator model alias. sase doctor reports a leftover
model_aliases.builtin.epic_creator setting so it can be removed. See
Provider migration notes for removed
alias names and current replacements.
The TUI also supports temporary, per-alias session-level provider/model overrides
(set from Launch Control, ,m) that do
not edit this config. They are persisted to ~/.sase/llm_override.json and expired
entries are deleted on next read. See
Temporary model overrides for the
resolution order, state-file format, and precedence relative to
SASE_MODEL_TIER_OVERRIDE.
The same panel's p=Providers flow manages temporary provider disables in
~/.sase/llm_provider_disables.json. This is runtime state, not configuration: it does
not add a disabled_providers key, does not edit llm_provider.provider, and does not
rewrite any alias. A hard disable is fail-closed: alias selectors skip that member,
temporary alias overrides targeting it pause until the disable clears or expires, and
direct explicit provider/model requests fail with an actionable diagnostic instead of
silently switching providers. A soft disable is spared in | pools while another
member can cover, never diverts a || fallback, and does not pause overrides or fail
explicit requests. See
Temporary Provider Disables.
llm_provider.usage_limit¶
Automatic usage-limit detection temporarily disables a provider when that provider's own
error output positively matches its configured usage-limit patterns. This is separate
from llm_provider.retry: provider-scoped usage-limit classification wins before retry
policy, while a plain transient 429 or other retryable error that does not match a
usage-limit pattern still follows retry/fallback policy.
llm_provider:
usage_limit:
enabled: true
disable_seconds: 86400
min_disable_seconds: 60
max_disable_seconds: 604800
honor_reset_hint: true
honor_usage_windows: true
notify: true
relaunch: true
relaunch_limit: 20
providers:
claude:
patterns:
- "you've hit your usage limit"
exclude_patterns:
- "usage limit approaching"
replace_patterns: false
disable_seconds: null
honor_reset_hint: null
honor_usage_windows: null
Generated reference for llm_provider.usage_limit
Automatic disables are written to the same machine-wide provider-disable state used by
Launch Control, with source: "usage_limit". They expire and self-clean like manual
disables, can be cleared early from Launch Control, and do not unregister or rewrite a
provider. If a fallback is available, retry/fallback may proceed only to a different
enabled provider; the disabled provider remains skipped until expiry or clearing. See
Usage-Limit Auto-Disable for
reset-hint parsing, notification, and replacement details.
llm_provider.usage_metrics¶
Subscription-capacity collection settings. This is distinct from
llm_provider.usage_limit, which classifies provider error text and writes temporary
routing disables. usage_metrics controls whether SASE probes installed provider CLIs
for included-allowance windows.
enabled is the durable user preference; setting it false stops probes, isolated
workers, passive observation writes, scheduled requests, and attention.
Claude, Codex, Grok, Muse Code, and Antigravity ship collectors. Muse's is a free local
probe against muse serve — no model call and no tokens — so it costs only about three
seconds of wall clock per refresh. To turn it off, set
llm_provider.usage_metrics.providers.muse.enabled to false; see
Muse Code subscription usage.
Antigravity's is likewise a free local /usage probe (agy >= 1.1.11, about three to
five seconds per refresh); see
Antigravity subscription usage.
Every recorded attempt carries its outcome, failure reason, and any Retry-After hint,
and refresh backoff follows the failure class. sase usage list -v shows the
per-provider retry state; JSON output passes the collector_health snapshot through
unchanged.
| Class | Failure reasons | Retry policy |
|---|---|---|
| Success | ok, not_applicable, unsupported outcomes |
next probe at the provider cadence |
| Transient | timeout, deadline_exceeded, probe_failed, parse_error, malformed_payload, account_context_changed |
cadence-based backoff up to 30 minutes |
| Rate-limited | rate_limited |
honored Retry-After (clamped to 15 minutes–6 hours) when present, otherwise an escalating delay from 15 minutes up to 2 hours |
| Auth | unauthenticated, logged_out, api_mode outcomes |
generic backoff up to 30 minutes |
| Parked | not_installed, unsupported_cli_version |
6-hour backoff, released early when the CLI changes |
| Vendor drift | vendor_drift |
fixed 1-hour backoff |
Providers in active use refresh on the hot cadence instead of the idle one. A provider
counts as hot while a recent agent launch or limit-event hint (each good for 15 minutes)
is still live, or while one of its stored un-reset windows sits at or above
warn_percent used. Hotness is suppressed while live stream events already keep every
stored window fresh — a window received within the active cadence of now needs no probe.
The hot interval is max(active_refresh_seconds, floor), so a hot Claude still waits
for its 300 s floor while a hot Codex is due at about 120 s.
llm_provider:
usage_metrics:
enabled: true
refresh_seconds: 300
active_refresh_seconds: 120
warn_percent: 75
critical_percent: 90
indicator:
enabled: true
default:
below_remaining_percent: 20
weekly_all: always
providers:
muse:
windows:
session: never
providers:
claude:
enabled: true
Generated reference for llm_provider.usage_metrics
An indicator policy is exactly one of:
always: display each matching observed window at any remaining percentage.never: suppress matching display entries.{below_remaining_percent: N}: display only when the unrounded remaining percentage is strictly less than finiteN, where0 <= N <= 100.
Thresholds use percentages remaining, not percentages used. 0 selects no numeric
windows, 100 still excludes an exactly full window, and always is the only policy
that includes full capacity.
Display policy precedence is exact window key, provider default, weekly_all for a
positively classified weekly all-model window, then global indicator.default. Provider
and window IDs are open-ended; unmatched future provider/window keys are inert.
The bundled default adds one exact Muse override:
llm_provider:
usage_metrics:
indicator:
providers:
muse:
windows:
session: never
Claude's observed weekly Fable window has no bundled entry, so the global
indicator.default threshold governs it and the header shows it only when its remaining
capacity is strictly below 20%. Set the exact key "weekly:claude-fable-5" to always
to restore the always-visible behavior, to never to hide it even when low, or to a
{below_remaining_percent: N} of its own. Missing or collection-ineligible Fable
windows are not synthesized. Because config layers merge recursively, an empty user
indicator.providers: {} does not erase this bundled key, while an explicit value for
the key does override it. Broader provider/global defaults have lower selection
precedence than the exact window key.
Muse reports two windows: a 5-hour session window and a weekly window. The header
shows only the weekly one by default. Muse's weekly window is classified as a weekly
all-model window, so the existing weekly_all: always policy selects it with no entry
of its own, and it appears at any remaining percentage once Muse is an eligible provider
(the muse CLI is installed and Muse is referenced by a model alias or enabled in
llm_provider.usage_metrics.providers). The session: never override deliberately
suppresses the 5-hour window even when it runs low; it stays visible in Providers ·
Usage and sase usage list -p muse. Restore header attention for it with:
llm_provider:
usage_metrics:
indicator:
providers:
muse:
windows:
session:
below_remaining_percent: 20
Antigravity reports four windows — gemini-weekly, gemini-5h, 3p-weekly, and
3p-5h — and has no bundled indicator entry. Its Gemini weekly window is treated as the
provider's weekly anchor (it matches the weekly_all policy while keeping its
family:gemini scope), so weekly_all: always anchors it in the header unlabeled
whenever agy is an eligible provider; a providers.agy.default policy would take
precedence. The Gemini 5-hour window and both 3p-* (Claude/GPT) windows fall through
to the global indicator.default threshold and appear only below 20% remaining. Pin or
hide them with exact keys:
llm_provider:
usage_metrics:
indicator:
providers:
agy:
windows:
gemini-5h: always
3p-weekly: never
Invalid indicator config is diagnosed with its config path, ignored at the smallest invalid override, and inherited/default policy is used instead of resetting unrelated valid usage settings. sase's TUI display settings are cached by the merged-config token, so config changes reload on the normal usage refresh cadence even when the usage-state file does not change.
Routing-disabled providers still refresh when collection is otherwise eligible, because reset information remains useful for them.
The same panel's fixed Ctrl+E binding manages the separate machine-wide default-effort
override at ~/.sase/llm_effort_override.json. It uses the alias override duration and
exact-time cards, but its state and precedence are independent: explicit prompt effort
and alias/member effort win, then the temporary effort override, then
llm_provider.default_effort, then the provider default. See
Reasoning Effort.
Its fixed Ctrl+R binding manages max_running_agents: persistent edits target the
user-base sase.yml (or its chezmoi source), while temporary values live independently
in ~/.sase/max_running_agents_override.json. This is a Launch Control binding, not an
ace.keymaps option.
llm_provider.continuation_budget¶
Byte budget for the continuation-budget preflight. Monitor successors always run it, and
so does any invocation with SASE_CONTINUATION_BUDGET_ENFORCE=1. The preflight measures
the final expanded prompt; when its essential content cannot fit the effective budget,
SASE records the decision and refuses before calling the provider.
llm_provider:
continuation_budget:
context_limit_bytes: 800000
estimate_uncertain: true
instruction_reserve_bytes: 0
tool_reserve_bytes: 0
output_reserve_bytes: 0
reasoning_reserve_bytes: 0
providers:
agy:
transport_limit_bytes: 122880
instruction_reserve_bytes: 512
# models:
# <model-name>: { context_limit_bytes: 400000 }
Generated reference for llm_provider.continuation_budget
Each field resolves independently: a matching model entry wins over its provider entry,
which wins over the shared values, and the matching SASE_CONTINUATION_* variable (see
LLM Provider environment variables) outranks
all of them. A zero, negative, or non-integer value uses the built-in default instead.
The bundled agy entry reflects that Antigravity sends the print prompt as one argv
element; SASE keeps those agy transport and instruction-reserve values even without
configuration and never lets the agy instruction reserve drop below 512 bytes.
Source:
src/sase/llm_provider/continuation_budget_request.py
llm_provider.retry¶
Per-provider retry and fallback configuration. See Provider retry and fallback for the full retry flow and TUI display.
llm_provider:
retry:
claude:
max_retries: 3
error_patterns:
- "API Error: 500"
wait_times: [60, 300, 1800]
fallback_model: "sonnet"
continuation_prompt: "Please continue from the last preserved work."
preserve_workspace: true
spawn_new_agent: false
Generated reference for llm_provider.retry
Configured retry policy is merged with provider-supplied retry defaults when a provider
declares them. For list fields such as error_patterns, built-in patterns are kept and
configured patterns are appended with duplicates removed. Claude's provider hook adds
workspace-preserving matching for context-limit, socket-close, and Claude CLI API-error
output, plus a continuation nudge. Those hook defaults are merged with the bundled
Claude policy in default_config.yml, so the configured wait times and fallback model
still apply unless you override them.
Source: src/sase/llm_provider/retry_config.py, src/sase/llm_provider/config.py
Related¶
- LLM provider configuration — generated provider keys and defaults.
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.