description: "Models settings and product-onboarding plugin for the dsh web client: provider rows, API-key management, model lists, and the DeepSeek first-run dialogs."
English | 中文
dsh-client-ui-settings-models is the Models settings page of the dsh web client: users configure API keys (stored write-only under the profile's credential reference), edit each provider's model list, and hand-declare custom pi-ai routes, with provider rows and one editor card at a time. The page joins the provider directory, the settings document, and the credential descriptions into one shared snapshot, so a row's state stays consistent across all three. It also walks first-run users through two ordered dialogs — a versioned internal-testing notice and the conditional official-DeepSeek credential step.
Open the Models page from the Settings navigation to see every configured provider as a row. A whole-section provider whose key is not configured anywhere renders as its open setup card instead, but only in the first-run posture and only until the user closes that card. Each card kind owns its own open state, so closing one never discards a draft in another.
A provider with a stored catalog error remains visible with its diagnostic and edit/delete actions. Add actions are offered only for registered settings namespaces, so an unavailable namespace cannot leave a button that opens no editor. A rejected save leaves the editor open and displays the Host diagnostic.
The primary field on an editor card 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. Leaving a new pi-ai provider's key blank saves a reference-free profile and preserves provider-native authentication (for example the Bedrock credential chain or Vertex ADC). A row labels API-key state with a green solid dot only when a referenced credential is confirmed configured, and with a red solid dot only when a named reference is confirmed missing. A successful Apply emits a local accessible status message without echoing secret material.
The collapsed 自定义设置 fold carries the curated extras: baseURL for both families (the deepseek placeholder shows the public endpoint), each adapter's model catalog, and the display name and API protocol of a pi-ai route the adapter does not ship. Profile headers remain deployment configuration in settings.yaml or Cordis config and have no Models-page editor. The Provider ID stays fixed: it is the settings key, the name every other namespace and every logged session references, and the stem of a credential reference the page cannot read back to move. Reasoning effort is deliberately not among the editable fields: it is a per-model capability, so a provider-scoped control could only be set to a value some models reject. Each DeepSeek row edits id, optional display name, and optional contextWindow/maxTokens; existing fields outside that curated set survive edits.
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. Add a custom provider declares a route pi-ai does not ship; the create card asks for a unique Provider ID, an endpoint, a protocol, and at least one uniquely-identified model, because nothing can default those. The endpoint must be a parseable HTTP or HTTPS URL; localhost, IPv4 and IPv6 literals, and custom ports remain valid. A syntax error blocks both discovery and creation at the field, while a request failure remains a separate provider error. Fetch available models asks the llm/discoverModels Remote about the endpoint the form shows, so adding a provider is one pass instead of save-then-return; the reply opens a searchable picker rather than being written, and nothing is written until Add selected. Each selected candidate copies its id, display name, context window, and output-token cap into the editable row when disclosed, while an existing row retains its user-tuned values. Search matches model ids and optional display names without clearing hidden selections. Select all adds the visible results, while Deselect all clears the entire selection so hidden results cannot be adopted accidentally. A row is deletable only when the user layer alone carries it (removal restores the composition base), and its confirmation dialog names the provider.
After the versioned notice step completes, the DeepSeek step projects first-run readiness from the same joined snapshot. ANY provider the user can already reach ends it without rendering; only a user with none is asked for the official DeepSeek key. Configure later completes only this coordinator pass, and an absent adapter, inactive route, failed join, read-only deployment, or unusable capability completes the step without rendering — Models remains the diagnostic surface.
The section declares two seats for plugins distributed outside this repository, typed in src/client/slot-contract.ts and exported from ./client. settings.models.provider-card (keyed) renders inside every card that shows a directory row — a saved row's card, its first-run setup posture, and the add-provider draft — dispatched with entryKey = settingsNs and owner props carrying the row's ConfigurableProviderView, its configured state, and its confirmed api-key credential state, so one registration under an adapter family's namespace receives every card of that family, hand-declared routes included; the hand-declared draft card has no directory row yet and dispatches nothing until saved. settings.models.footer (list) renders after the rows and the add controls. A registrant activates through ctx.slots.inject with a type-only import of this package's /client entry; without registrants both seats render nothing.
These pages cover the settings base, the seams this page joins, and the design rationale.
None, as the package is a browser-side UI plugin layer that registers nothing model-facing.
None; this package neither assembles nor sends a provider request.
These limits define the editor's field coverage and the page's reach; they are current package constraints, not a settings roadmap.
settings.yaml; existing model fields the editor does not show are preserved.<ROUTE>_API_KEY target this page derives. Custom references, environment credentials, and unidentifiable targets are retained because the row cannot prove ownership of them.llm-pi-ai, the one namespace whose profiles describe a whole provider. A llm-deepseek route is a composition fact, not something this page can create.data array or an enriched models map, while Anthropic uses its native model-listing route; every other protocol reports that it cannot be asked and its models are entered by hand.Runtime invariant: No companion is published. A nav-entry-only section plugin rendering a fixed empty content column — it emits no cordis events and owns no cross-plugin mutable relation.