feat(agent): make model presets session-scoped (#4866)
This commit is contained in:
@@ -9,7 +9,7 @@ These commands work inside chat channels and interactive agent sessions:
|
||||
| `/restart` | Restart the bot |
|
||||
| `/status` | Show bot status |
|
||||
| `/model` | Show the current model and available model presets |
|
||||
| `/model <preset>` | Switch the runtime model preset for future turns |
|
||||
| `/model <preset>` | Switch and persist the model preset for the current session |
|
||||
| `/dream` | Run Dream memory consolidation now |
|
||||
| `/dream-log` | Show the latest Dream memory change |
|
||||
| `/dream-log <sha>` | Show a specific Dream memory change |
|
||||
@@ -47,7 +47,7 @@ Use `/model` to inspect the current runtime model:
|
||||
/model
|
||||
```
|
||||
|
||||
The response shows the current model, the current preset, and the available preset names. Named presets come from the top-level `modelPresets` config and are the recommended way to configure model choices. `default` is always available and represents the model settings from direct `agents.defaults.*` fields.
|
||||
The response shows the current session's model and preset, plus the available preset names. Named presets come from the top-level `modelPresets` config and are the recommended way to configure model choices. `default` is always available and represents the model settings from direct `agents.defaults.*` fields.
|
||||
|
||||
To switch presets for future turns:
|
||||
|
||||
@@ -57,7 +57,7 @@ To switch presets for future turns:
|
||||
/model default
|
||||
```
|
||||
|
||||
Preset names come from the top-level `modelPresets` config. Switching is runtime-only: it does not rewrite `config.json`, and an in-progress turn keeps using the model it started with. See [Configuration: Model presets](./configuration.md#model-presets) for setup details.
|
||||
Preset names come from the top-level `modelPresets` config. Switching affects only the current session and persists the selection in that session, so later turns keep using it across process restarts. It does not rewrite `config.json`, does not change other sessions, and does not alter an in-progress turn's captured model. Sessions without a saved selection follow `agents.defaults.modelPreset` (or the implicit `default` preset when it is omitted). See [Configuration: Model presets](./configuration.md#model-presets) for setup details.
|
||||
|
||||
## Local triggers
|
||||
|
||||
|
||||
@@ -1298,7 +1298,7 @@ Contributor notes for adding new providers live in [`development.md`](./developm
|
||||
|
||||
## Model Presets
|
||||
|
||||
Model presets let you name a complete model configuration and switch it at runtime with `/model <preset>`. They are the recommended way to configure models because the same names can be reused for startup selection, chat-command switching, and fallback chains.
|
||||
Model presets let you name a complete model configuration and select one per session with `/model <preset>`. They are the recommended way to configure models because the same names can be reused for new-session defaults, chat-command switching, and fallback chains.
|
||||
|
||||
Existing configs do not need to change. Direct `agents.defaults.model`, `provider`, `maxTokens`, `contextWindowTokens`, `temperature`, and `reasoningEffort` fields still define the implicit `default` preset. For new configs, prefer top-level `modelPresets` plus `agents.defaults.modelPreset`.
|
||||
|
||||
@@ -1362,7 +1362,7 @@ Existing configs do not need to change. Direct `agents.defaults.model`, `provide
|
||||
|
||||
`default` is reserved and always means the implicit preset built from direct `agents.defaults.*` fields; do not define `modelPresets.default`. Use `/model default` to switch back to those direct fields in an existing config.
|
||||
|
||||
Set `agents.defaults.modelPreset` to choose the startup preset. When `modelPreset` is `null` or omitted, startup uses the implicit `default` preset from direct `agents.defaults.*` fields. Runtime changes made with `/model <preset>` are not written back to `config.json`; they affect future turns until the process restarts or another model/config change replaces them.
|
||||
Set `agents.defaults.modelPreset` to choose the preset followed by sessions that have no saved model selection. When `modelPreset` is `null` or omitted, such sessions follow the implicit `default` preset from direct `agents.defaults.*` fields. `/model <preset>` saves an override in the current session, so its future turns keep that preset across process restarts while other sessions remain unchanged. The command does not write the selection back to `config.json`.
|
||||
|
||||
### Model Fallbacks
|
||||
|
||||
|
||||
+16
-15
@@ -27,7 +27,8 @@ To allow the agent to set its configuration (e.g. switch models, adjust paramete
|
||||
|
||||
Legacy `tools.myEnabled` / `tools.mySet` keys are auto-migrated on load, and rewritten in-place the next time `nanobot onboard` refreshes the config.
|
||||
|
||||
All modifications are held in memory only — restart restores defaults.
|
||||
Most modifications are held in memory only. `model_preset` is the exception: it is
|
||||
stored in the current session so the selection survives a restart.
|
||||
|
||||
---
|
||||
|
||||
@@ -77,20 +78,18 @@ my(action="check", key="web_config.enable")
|
||||
|
||||
## set — Runtime tuning
|
||||
|
||||
Changes take effect immediately, no restart required.
|
||||
Changes do not require a restart. `model_preset` is saved for the current session and
|
||||
applies to its next turn; other writable runtime tuning takes effect immediately.
|
||||
Direct `model` and `context_window_tokens` writes are rejected during an active session
|
||||
because those setters change the shared instance default. Configure a named preset for
|
||||
model or context-window changes instead.
|
||||
|
||||
```text
|
||||
my(action="set", key="max_iterations", value=80)
|
||||
# → Bump iteration limit from 40 to 80
|
||||
|
||||
my(action="set", key="model_preset", value="fast")
|
||||
# → Switch to a configured model preset
|
||||
|
||||
my(action="set", key="model", value="fast-model")
|
||||
# → Switch to a raw model and clear the active preset
|
||||
|
||||
my(action="set", key="context_window_tokens", value=262144)
|
||||
# → Expand context window for long documents
|
||||
# → Use a configured model preset for this session's next turn
|
||||
```
|
||||
|
||||
You can also store custom state in your scratchpad:
|
||||
@@ -109,9 +108,9 @@ These parameters have type and range validation — invalid values are rejected:
|
||||
| Parameter | Type | Range | Purpose |
|
||||
|-----------|------|-------|---------|
|
||||
| `max_iterations` | int | 1–100 | Max tool calls per conversation turn |
|
||||
| `context_window_tokens` | int | 4,096–1,000,000 | Context window size |
|
||||
| `model` | str | non-empty | LLM model to use |
|
||||
| `model_preset` | str | configured preset name | Named preset to use |
|
||||
| `context_window_tokens` | int | 4,096–1,000,000 | Instance default; during a session, select through a preset |
|
||||
| `model` | str | non-empty | Instance default; during a session, select through a preset |
|
||||
| `model_preset` | str | configured preset name | Current session's preset for its next turn |
|
||||
|
||||
Other parameters (e.g. `workspace`, `provider_retry_mode`, `max_tool_result_chars`) can be set freely, as long as the value is JSON-safe.
|
||||
|
||||
@@ -122,8 +121,8 @@ Other parameters (e.g. `workspace`, `provider_retry_mode`, `max_tool_result_char
|
||||
### "This task is complex, I need more room"
|
||||
|
||||
```text
|
||||
Agent: This codebase is large, let me expand my context window to handle it.
|
||||
→ my(action="set", key="context_window_tokens", value=262144)
|
||||
Agent: This codebase is large, let me switch this session to the configured deep preset.
|
||||
→ my(action="set", key="model_preset", value="deep")
|
||||
```
|
||||
|
||||
### "Simple question, don't waste compute"
|
||||
@@ -180,7 +179,9 @@ Agent: The code review is progressing well. The test task hasn't started yet.
|
||||
|
||||
## Safety Mechanisms
|
||||
|
||||
Core design principle: **All modifications live in memory only. Restart restores defaults.** The agent cannot cause persistent damage.
|
||||
Core design principle: **The tool does not rewrite `config.json`.** Instance-wide
|
||||
changes live in memory only, while `model_preset` persists only as the current
|
||||
session's selector.
|
||||
|
||||
### Off-limits (BLOCKED)
|
||||
|
||||
|
||||
@@ -610,7 +610,9 @@ In chat:
|
||||
/model fast
|
||||
```
|
||||
|
||||
`/model` switching is runtime-only. It does not rewrite `config.json`, and an in-progress turn keeps using the model it started with.
|
||||
`/model` stores the selection in the current session without rewriting `config.json`.
|
||||
The selection survives restarts, does not affect other sessions, and an in-progress
|
||||
turn keeps using the model it started with.
|
||||
|
||||
## Quick Failure Map
|
||||
|
||||
|
||||
+7
-5
@@ -494,8 +494,10 @@ Run the agent once and return a `RunResult`.
|
||||
| `model` | `str \| None` | `None` | Override the model for this run only. |
|
||||
| `model_preset` | `str \| None` | `None` | Override the model preset for this run only. |
|
||||
|
||||
`model` and `model_preset` are per-run overrides and do not change
|
||||
`bot.runtime.model` after the run completes. They are mutually exclusive.
|
||||
Without an override, a run uses the preset saved in its session, or the configured
|
||||
default when that session has no saved selection. `model` and `model_preset` are
|
||||
mutually exclusive per-run overrides; they do not change the saved session selection
|
||||
or `bot.runtime.model` after the run completes.
|
||||
|
||||
### `await bot.run_streamed(...)`
|
||||
|
||||
@@ -531,9 +533,9 @@ async for event in bot.stream("Generate a long answer"):
|
||||
| `await cancel()` | Cancel the run and release stream resources. |
|
||||
| `await aclose()` | Close the stream; equivalent cleanup primitive for `async with` / manual lifecycle code. |
|
||||
|
||||
Normal SDK runs with different session keys may overlap. Runs that use per-run
|
||||
`model` or `model_preset` overrides are exclusive while the override is active,
|
||||
because the current `AgentLoop` provider/model state is mutable.
|
||||
SDK runs with different session keys may overlap, including runs with per-run
|
||||
`model` or `model_preset` overrides. Each run receives an immutable runtime without
|
||||
mutating the instance default. Runs sharing one session key remain serialized.
|
||||
|
||||
### `StreamEvent`
|
||||
|
||||
|
||||
+6
-2
@@ -152,7 +152,8 @@ All frames are JSON text. Each message has an `event` field.
|
||||
|
||||
Reasoning frames only flow when the channel's `showReasoning` is `true` (default) and the model returns reasoning content (DeepSeek-R1 / Kimi / MiMo / OpenAI reasoning models, Anthropic extended thinking, or inline `<think>` / `<thought>` tags). Models without reasoning produce zero `reasoning_delta` frames.
|
||||
|
||||
**`runtime_model_updated`** — broadcast when the gateway runtime model changes, for example after `/model <preset>`:
|
||||
**`runtime_model_updated`** — broadcast when the gateway default runtime changes or
|
||||
when a config reload requires clients to refresh their model catalog:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -162,7 +163,10 @@ Reasoning frames only flow when the channel's `showReasoning` is `true` (default
|
||||
}
|
||||
```
|
||||
|
||||
`model_preset` is omitted when no named preset is active. WebUI clients use this event to keep the displayed model badge in sync across slash commands, config reloads, and settings changes.
|
||||
`model_preset` is omitted when no named preset is active. WebUI clients use this event
|
||||
to refresh model settings after default-runtime and config changes. `/model <preset>`
|
||||
is session-scoped; its selection is reflected through `session_updated` and the
|
||||
session row's `model_preset` field instead of this global event.
|
||||
|
||||
**`attached`** — confirmation for `new_chat` / `attach` inbound envelopes (see [Multi-chat multiplexing](#multi-chat-multiplexing)):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user