The typed text was held in one active buffer, so editing a second row displaced the first — which then fell back to rendering its stored NaN as the literal `NaN`, losing the text the row was supposed to let the user correct. Reset dropped the override but kept the buffer, leaving typed text over an inherited row that no settings layer stores, and an unreadable buffer never settles, so it stayed there. There is now one entry per row. `remove` re-keys around the dropped row so text travels with its own row, and reset clears them all because the rows they annotated are gone. Model ids are compared trimmed as well: `" "` passed the required check, and `"model "` slipped past the duplicate check against its own twin before silently failing to match at the adapter. A pasted id settles on blur rather than being trimmed per keystroke, which would stop an interior space.
@deepseek-ai/dsh-client-ui-models
English | 中文
Models settings plugin: the provider configuration page and official-DeepSeek first-run routing overlay. It joins three wire domains into one shared snapshot — llm.providers (the configurable-provider directory with each route's live/dormant state), settings.describe (serialized schemas, layered redacted values, secret slots), and credentials.describe (value-free configured/source/writable badges) — and renders provider rows with one editor card at a time, without presenting route liveness as provider status.
Rows are the configured providers (their profile resolves in the owning namespace); a whole-section provider whose key is not configured anywhere (the first-run DeepSeek posture) renders as its open setup card instead of a row, and the add flow is a card carrying the dormant-directory provider select — a bare-mounted llm-pi-ai offers its whole installed catalog before any route exists. The editor is a hand-written card per adapter family: the primary field is a single API key input — the page never asks for an environment-variable name; a typed key stores write-only through credentials.set under the profile's reference, deriving <ROUTE>_API_KEY when the profile has none, and the pi-ai profile records that derivation as apiKeyEnv, so settings.yaml never carries a key value. The collapsed 自定义设置 fold carries the curated extras — baseURL for both families (the deepseek placeholder shows the public endpoint), reasoningEffort (deepseek) or reasoning (pi-ai), and the direct DeepSeek adapter's advisory model catalog. Each DeepSeek row edits id, optional display name, and optional contextWindow; existing fields outside that curated set survive edits, while every other profile field stays owned by settings.yaml. A row is deletable only when the user layer alone carries it (removal restores the composition base), and a localized confirmation dialog must complete before the page submits that destructive unset.
The first-run overlay projects deepseek-official readiness from that same joined snapshot. It recognizes the official adapter through its llm-deepseek configurable-provider declaration, so an undeclared live route with the same provider id is not treated as repairable configuration. A configured literal apiKey secret sidecar or configured credential reference suppresses the prompt, including a read-only launch-environment credential. Only a mounted adapter with a missing writable reference shows the action that opens Settings on the Models section, whose existing setup card exclusively owns key input and credentials.set; the overlay never holds a secret. An absent adapter, inactive route, failed join, read-only deployment, or unusable settings or credential capability is skipped so onboarding cannot block the rest of the product; the Models page remains the diagnostic surface.
Every edit lands as settings.mutate path ops against the stored section — a set per changed field, an unset per cleared one, and a single unset for a deleted provider row. The page only ever holds the REDACTED descriptor, so it names the fields it can see rather than rebuilding a section: a stored literal secret it never received is mentioned by no op and survives. DeepSeek's models is one replace-by-value array: the editor shows inherited effective rows until the first model edit materializes the complete array in the user layer, while reset unsets that override. A context window is typed as a count with an optional decimal K or M suffix (256K, 1M; 1M is 1000K) and stored as the plain count, spelled back in the shortest form that round-trips. Empty ids, duplicate ids, empty explicit names, and unreadable, non-positive, or fractional context windows fail before any write. Each write carries the revision the card opened at, so a concurrent write from another tab or an external settings.yaml edit is refused as settings-conflict and the card asks the user to reopen instead of replaying its stale snapshot. The page refetches on the pushed invalidations (settings/changed, credentials/changed, models/changed, and connection/reset) once it has loaded, so an external settings.yaml edit, a second tab, or a settings-born route converges without polling.
Model Experience
None, as the section renders a browser configuration UI; nothing here reaches a model request.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known Limitations and Deferred Work
- Only the API key and curated fold fields are editable on the card — the hand-written editor traded schema-generic field coverage for the mockup layout (Agent Note). DeepSeek exposes
baseURL,reasoningEffort, and modelid/name/contextWindow; pi-ai exposesbaseURLandreasoning. Retry policy, timeouts, DeepSeek model descriptions, and other advanced fields remain insettings.yaml; existing model fields the editor does not show are preserved. A profile schema without the conventional fields renders the hint alone, and the two curated layouts key on thellm-deepseek/llm-pi-ainamespaces by name. - Deleting a row leaves its stored key in
.env— removal unsets the settings profile but deliberately does not unset the derived credential; re-adding the provider finds the key already configured. An explicit key-removal control is deferred. - Undeclared live routes render nowhere — a route registered without a configurable-provider declaration has no settings address; it stays visible in pickers but not on this page's rows.