# Configuration Reference [完整中文说明](./configuration.zh.md) Everything is configured from the Settings page (**Settings → 模型中枢 / Model Hub**). Model providers themselves are configured **only** on the DSH Models settings page; this page owns routing policy and media channels. Changes apply **live**; the settings persist to the `model-router` settings namespace (`~/.dsh/settings.yaml`). ## Top level | Field | Type | Default | Description | |---|---|---|---| | `schemaVersion` | number | `2` | V2 marker; persisted only after a user-confirmed save. | | `enabled` | boolean | `true` | Master switch. Disabled = no rule engine, no `model_route`, no session writes. | | `matchCase` | boolean | `false` | Case sensitivity of keyword matching. | | `rules` | array | `[]` | Ordered keyword rules; first hit wins; disabled rules skipped. | | `agentSwitch` | object | `{enabled: false, allow: []}` | Controlled manual-switch tool config (default off). | | `mediaProviders` | array | `[]` | Isolated media channels (`image_gen` only). | ## Rules (`rules[]`) | Field | Type | Description | |---|---|---| | `id` | string | Stable unique id. | | `enabled` | boolean | Disabled rules are skipped. | | `keywords` | string[] | Trimmed non-empty keywords; any hit selects the rule. | | `target.provider` | string | NATIVE provider id (e.g. `deepseek-official`, `openai`). Dormant providers save but never execute. | | `target.model` | string | Exact model id; the advisory catalog only suggests, `resolveModelInfo` decides. | | `target.reasoningEffort` | string? | Adapter-owned opaque string; validated by `resolveCallConfig`; never a fixed enum. | Semantics: on a hit, the plugin validates `resolveModelInfo(provider, model)` (and `resolveCallConfig` when an effort is explicit) BEFORE writing the session `request/header`. Validation failure keeps the session unchanged and logs only provider/model/code. **No-match = no write** — there is no fallback in V2; the harness `agent-default-model` governs. ## agentSwitch | Field | Type | Description | |---|---|---| | `enabled` | boolean | Must be `true` AND `allow` non-empty for `model_route` to register. | | `allow` | RouteTarget[] | Exact `provider/model/effort` entries; the tool exposes ONLY these. Executions outside the allowlist fail `ROUTE_NOT_ALLOWED`; every execution still passes live exact validation. | ## Media channels (`mediaProviders[]`) | Field | Type | Required | Description | |---|---|---|---| | `id` | string | yes | Channel id (`[a-z0-9][a-z0-9._-]*`, unique). | | `name` | string | yes | Display name. | | `capabilities` | string[] | yes | Current implementation: `image_gen` only. | | `adapter` | string | yes | Current implementation: `openai-images` only. | | `baseUrl` | string | yes | OpenAI-compatible HTTPS endpoint. HTTP is allowed only for loopback development hosts; userinfo, query strings, and fragments are rejected. | | `credential` | string | no (default `''`) | Credential **reference**; empty = unauthenticated. The value never reaches the page/logs/snapshots. | | `models` | string[] | yes (≥1) | First entry is the default. | | `allowedResultHosts` | string[] | no | Bare hostnames allowed for generated-image downloads besides the endpoint host. | | `priceTier` | string | no | Free-form cost note. | | `defaultSize` | string | no | Default image size passed when `image_gen` does not specify one. | ### image_gen security bounds (Beta) - Result downloads: `https` only, no userinfo, default port, allowed hosts = endpoint host ∪ `allowedResultHosts`, max 3 redirects (each hop revalidated), private/reserved resolved addresses rejected, DNS pinning between check and connect. - Size: streaming cap of **20 MiB** (base64 payloads estimated before decode); request and idle-download timeouts. - Content: **magic bytes only** (PNG/JPEG/WebP/GIF); Content-Type is never trusted; unknown bytes fail `MEDIA_NOT_IMAGE`. - Artifact preview: realpath fence (root and target realpath'd, symlink escape refused), regular files only, ≤ 20 MiB, GET only, `nosniff` + private cache headers. - Provider requests: HTTPS by default; HTTP only on `localhost`, `127.0.0.0/8`, or `::1`. An empty credential sends no Authorization header. ## Stable error codes | Code | Meaning | |---|---| | `ROUTE_PROVIDER_INACTIVE` | Provider not in the active registry (no session write) | | `ROUTE_MODEL_UNRESOLVED` | `resolveModelInfo` failed (no session write) | | `ROUTE_REASONING_INVALID` | `resolveCallConfig` rejected the effort (no session write) | | `ROUTE_NOT_ALLOWED` | `model_route` / apply target outside the allowlist or rule targets | | `ROUTE_MIGRATION_REQUIRED` | Target still names a legacy V1 channel id — map it manually | | `MEDIA_PROVIDER_UNKNOWN` | Unknown media channel / generation failure | | `MEDIA_MODEL_NOT_ALLOWED` | Model not in the media channel's model list | | `MEDIA_DOWNLOAD_BLOCKED` | URL/redirect/host/DNS policy refused the download | | `MEDIA_TOO_LARGE` | Payload exceeds the size cap | | `MEDIA_NOT_IMAGE` | Bytes are not a magic-confirmed image | | `ARTIFACT_PATH_BLOCKED` | Preview path escaped the realpath fence | ## V1 → V2 migration The stored V1 section is migrated **in memory only** and shown as structured warnings; nothing is written at startup. Saving from the page persists `schemaVersion: 2` and removes the legacy fields. | V1 item | V2 result | |---|---| | Channel with `image_gen` tag | Media channel (`openai-images`, `image_gen`), fields kept | | Channel with `vision + image_gen` | Only `image_gen` migrated + warning to configure vision chat on the DSH Models page | | Channel with only `agent_text`/`agent_coding`/`vision` | Not migrated; warning with channel id | | Rule `then` | V2 rule with generated id; legacy channel ids marked `ROUTE_MIGRATION_REQUIRED` (never silently remapped) | | `fallback` | Deprecated with a warning; does not run | ## V2 YAML example ```yaml model-router: schemaVersion: 2 enabled: true matchCase: false rules: - id: summary-rule enabled: true keywords: [汇总, summary] target: provider: deepseek-official model: deepseek-v4-flash agentSwitch: enabled: false allow: [] mediaProviders: - id: volcengine-seedream name: 豆包 Seedream 5.0 Lite capabilities: [image_gen] adapter: openai-images baseUrl: https://ark.cn-beijing.volces.com/api/v3 credential: VOLCENGINE_ARK_API_KEY # reference name, never a value models: [doubao-seedream-5-0-lite-260128] allowedResultHosts: - ark-acg-cn-beijing.tos-cn-beijing.volces.com defaultSize: 1920x1920 ``` The conversation header identifies the **text model orchestrating the tool call**. The actual image generator is recorded in the `image_gen` tool result; for the example above it is `volcengine-seedream/doubao-seedream-5-0-lite-260128`. ## Recovering the previous version Before upgrading, back up your DSH settings locally. To recover, stop saving settings, restore the pre-migration backup (`settings.yaml.model-router-v1-.bak`), check out a published release tag or a known commit from `git log`, reinstall it with the DSH plugin command, and restart the web profile. Never attach the settings file to a public issue because it may contain credential references or private provider details.