🐾 fix: resolve registry capabilities per agent, not once globally
_build_registry built one capabilities dict from the global default_model and
attached it to every entry, so any agent with an `agents.<name>.model` or
`model_capabilities` override was advertised under the wrong model — even
though server.py applies those overrides at startup. Resolve capabilities per
agent, mirroring _register_unknown_models.
Also stop emitting null context_window / max_output_tokens when
model_capabilities is absent: _register_one_model registers the model with
131072 / 16384, so the registry now advertises those effective values. The
defaults are hoisted to module constants in server.py so the two cannot drift.
Verified against mentor's config: capabilities go from
{"context_window": null, "max_output_tokens": null} to {131072, 16384}, model
name unchanged. No checked-in deployment uses per-agent overrides today, so no
currently-published model changes.
Adds tests/test_registry.py (9 tests, first coverage for registry.py) and
documents agents.<name>.model / model_capabilities, which the agents.yaml
field table omitted entirely.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -192,6 +192,8 @@ agents:
|
||||
| `agents.<name>.port` | yes | Port for this agent's StreamableHTTP MCP server |
|
||||
| `agents.<name>.title` | no | Display name in registry. Default: `name.title()` |
|
||||
| `agents.<name>.description` | no | Description in registry |
|
||||
| `agents.<name>.model` | no | `provider.model-name` override for this agent. Overrides `default_model`, is applied to every agent in the module at startup, and is what the registry advertises for this entry |
|
||||
| `agents.<name>.model_capabilities` | no | Per-agent `{vision, context_window, max_output_tokens}` block. Overrides the top-level `model_capabilities`; the same defaults apply to omitted fields |
|
||||
| `agents.<name>.depends_on` | no | List of agent names that must start and become ready before this agent |
|
||||
| `agents.<name>.max_iterations` | no | Hard cap on agentic-loop turns per `send_message`. Default: `15`. fast-agent returns a partial answer once exceeded |
|
||||
| `agents.<name>.loop_repeat_threshold` | no | Halt the loop after this many consecutive identical `(tool, args) → result` rounds. Default: `3`. `0` disables the guard |
|
||||
@@ -429,7 +431,9 @@ Built dynamically from `agents.yaml` + `fastagent.config.yaml`:
|
||||
|
||||
### Capabilities
|
||||
|
||||
If `model_capabilities` is defined in `fastagent.config.yaml`, each registry entry includes a `capabilities` object with model name, vision support, context window, and max output tokens. This allows clients to make informed decisions about what an agent can handle.
|
||||
Each registry entry includes a `capabilities` object — model name, vision support, context window, and max output tokens — whenever a model is known for that agent, i.e. `agents.<name>.model` is set or `default_model` is defined in `fastagent.config.yaml`.
|
||||
|
||||
Capabilities are resolved **per agent**: the agent's own `model` and `model_capabilities` take precedence over the global values, and omitted fields fall back to the same defaults Pallas uses to register the model (`vision: false`, `context_window: 131072`, `max_output_tokens: 16384`). The published values therefore match what was actually registered with fast-agent's `ModelDatabase`, rather than being null whenever `model_capabilities` was left out. Clients use them to make informed decisions about what an agent can handle.
|
||||
|
||||
---
|
||||
|
||||
@@ -673,12 +677,13 @@ Pallas registers models not in fast-agent's built-in `ModelDatabase` at startup,
|
||||
The process:
|
||||
|
||||
1. Read `default_model` and `model_capabilities` from config
|
||||
2. Extract the model name (portion after the provider prefix dot)
|
||||
3. Check if `ModelDatabase` already knows this model — if so, skip
|
||||
4. Register with `ModelDatabase.register_runtime_model_params()`:
|
||||
2. Also read every `agents.<name>.model` from `agents.yaml`, using that agent's own `model_capabilities` when it declares one
|
||||
3. Extract the model name (portion after the provider prefix dot)
|
||||
4. Check if `ModelDatabase` already knows this model — if so, skip
|
||||
5. Register with `ModelDatabase.register_runtime_model_params()`:
|
||||
- `vision: true` → multimodal tokenization (`QWEN_MULTIMODAL`)
|
||||
- `vision: false` → text-only tokenization (`TEXT_ONLY`)
|
||||
- `context_window` and `max_output_tokens` from config (with sensible defaults)
|
||||
- `context_window` and `max_output_tokens` from config, defaulting to `131072` / `16384` — the same values the registry advertises
|
||||
|
||||
This avoids the brittle pattern of inferring capabilities from model name substrings, which breaks for custom or fine-tuned models with non-standard names.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user