diff --git a/packages/client/ui-models/README.i18n.yaml b/packages/client/ui-models/README.i18n.yaml index 1f81138171..ae296a91aa 100644 --- a/packages/client/ui-models/README.i18n.yaml +++ b/packages/client/ui-models/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-models/README.md -README.md: 9d9833cf269d6b2225605e5a4b095d291d97a6bd -README.zh.md: c19499a1e49a8d537a1722ab4dc4c4b6f0ee2026 +README.md: b55914197e472edec8a8b6d4d3e02036d1697728 +README.zh.md: ca93c3d5a2a85fffb22707f8389f1e979468e2ec diff --git a/packages/client/ui-models/README.md b/packages/client/ui-models/README.md index 9d9833cf26..b55914197e 100644 --- a/packages/client/ui-models/README.md +++ b/packages/client/ui-models/README.md @@ -12,7 +12,7 @@ Every edit lands as `settings.mutate` path ops against the stored section — a ## Model list and endpoint interrogation -A pi-ai profile's `models` list is edited on the card: one row per model showing its id and display name, with the context window and output cap behind a per-row disclosure and two label-free actions — expand and delete — on the right. An empty list means "serve this route's built-in catalog", so a row is only ever added deliberately; clearing a capacity drops it rather than storing a value the schema would reject, and the adapter's route-level fallbacks size whatever configuration leaves out. A capacity that is not a positive integer is simply not stored. +A pi-ai profile's `models` list is edited on the card: one row per model showing its id and display name, with the context window and output cap behind a per-row disclosure and two label-free actions — expand and delete — on the right. An empty list means "serve this route's built-in catalog", so a row is only ever added deliberately; clearing a capacity drops it rather than storing a value the schema would reject, and the adapter's route-level fallbacks size whatever configuration leaves out — an empty capacity shows those fallbacks' magnitude as its placeholder, a hint rather than a mirror, since the field counts `K` as 1000 and a deployment may override them. A capacity that is not a positive integer is simply not stored. **Fetch available models** asks `llm.discoverModels` about the endpoint the form **currently shows**, including a base URL edited but not yet saved and a key typed but not yet stored, so adding a provider is one pass instead of save-then-return. The reply opens a picker rather than being written: candidates already configured start unchecked, so adopting a selection never overwrites a capacity the user corrected. A provider that cannot be interrogated is a detour, not a dead end — the adapter's own message appears beside the rows, which stay editable by hand. diff --git a/packages/client/ui-models/README.zh.md b/packages/client/ui-models/README.zh.md index c19499a1e4..ca93c3d5a2 100644 --- a/packages/client/ui-models/README.zh.md +++ b/packages/client/ui-models/README.zh.md @@ -12,7 +12,7 @@ ## 模型列表与端点询问 -pi-ai profile 的 `models` 列表就在卡片上编辑:一行一个模型,行上显示 id 与显示名称,上下文窗口与输出上限收在该行的展开区内,右侧是两个无文字的操作——展开与删除。空列表意味着「使用该路由的内置 catalog」,因此每一行都只会被刻意添加;清空容量会丢弃它,而不是存入一个 schema 会拒绝的值,配置留空的部分由适配器的路由级回退值定尺寸。不是正整数的容量根本不会被存下。 +pi-ai profile 的 `models` 列表就在卡片上编辑:一行一个模型,行上显示 id 与显示名称,上下文窗口与输出上限收在该行的展开区内,右侧是两个无文字的操作——展开与删除。空列表意味着「使用该路由的内置 catalog」,因此每一行都只会被刻意添加;清空容量会丢弃它,而不是存入一个 schema 会拒绝的值,配置留空的部分由适配器的路由级回退值定尺寸——留空的容量以这些回退值的量级作为占位符,那只是提示而非镜像:该字段按 1000 计 `K`,且部署可以覆盖这些回退值。不是正整数的容量根本不会被存下。 **获取可用模型**会针对表单**当前显示**的端点调用 `llm.discoverModels`,包括已修改但尚未保存的 API 地址和已键入但尚未存储的密钥,因此新增一个提供方是一趟走完,而不是「先保存再回来」。回复会打开一个选择框而不是直接写入:已配置过的候选默认不勾选,因此采纳一次选择绝不会覆盖用户已更正的容量。无法被询问的提供方只是绕路而非死路——适配器自己的消息会显示在各行旁边,而这些行仍可手工编辑。 diff --git a/packages/client/ui-models/src/client/ModelListEditor.tsx b/packages/client/ui-models/src/client/ModelListEditor.tsx index 27f706f642..6e7a26338c 100644 --- a/packages/client/ui-models/src/client/ModelListEditor.tsx +++ b/packages/client/ui-models/src/client/ModelListEditor.tsx @@ -109,6 +109,22 @@ function IconTrash(): ReactNode { /** The two token counts edited as K/M-suffixed text behind a row's disclosure. */ type CapacityField = 'contextWindow' | 'maxTokens' +/** + * What an empty capacity field is worth, shown as its placeholder so a row left + * blank does not read as a model with no capacity at all. + * + * The magnitudes are the adapter's own route-level fallbacks (`llm-pi-ai`'s + * `defaultContextWindow` and `defaultMaxTokens`), spelled the way a person + * would say them. They are a hint, not a mirror: this page counts `K` as 1000, + * so typing `256K` stores 256000 while leaving the field blank keeps the + * adapter's 262144. A deployment that overrides those defaults is not + * reflected here — nothing on this page can read them. + */ +const CAPACITY_HINT: Readonly> = { + contextWindow: '256K', + maxTokens: '32K', +} + /** * Spell a stored count for a field that may be unset. The spelling itself is * {@link formatCapacity}, shared with the DeepSeek catalog editor so both @@ -373,6 +389,7 @@ export function ModelListEditor(props: ModelListEditorProps): ReactNode { type="text" inputMode="numeric" value={capacityText(index, 'contextWindow')} + placeholder={CAPACITY_HINT.contextWindow} aria-label={`${t('modelContextWindow')} ${index + 1}`} disabled={disabled} onChange={(event) => { editCapacity(index, 'contextWindow', event.target.value) }} @@ -385,6 +402,7 @@ export function ModelListEditor(props: ModelListEditorProps): ReactNode { type="text" inputMode="numeric" value={capacityText(index, 'maxTokens')} + placeholder={CAPACITY_HINT.maxTokens} aria-label={`${t('modelMaxTokens')} ${index + 1}`} disabled={disabled} onChange={(event) => { editCapacity(index, 'maxTokens', event.target.value) }} diff --git a/packages/client/ui-models/src/client/ModelsSection.module.css b/packages/client/ui-models/src/client/ModelsSection.module.css index e8232e2d81..a4d4d04121 100644 --- a/packages/client/ui-models/src/client/ModelsSection.module.css +++ b/packages/client/ui-models/src/client/ModelsSection.module.css @@ -273,14 +273,17 @@ } .addButton { - /* Master's pill shape and glyph gap, sized to share the row equally so the - two ways to gain a provider read as siblings and line up with the rows - above rather than as two pills of different lengths. */ - display: inline-flex; - align-items: center; - justify-content: center; + /* Overrides the shared button base above: these two are not pills sitting in + a footer but the last slot of the provider list, so they split the row + evenly and repeat the row cards' corner. Dashed, like every other "nothing + here yet" affordance on this page, to read as a place rather than a + command. */ + flex: 1 1 0; + min-width: 180px; gap: 6px; - align-self: flex-start; + height: 44px; + border: 1px dashed var(--dsw-alias-border-l3); + border-radius: 12px; } .addCard, diff --git a/packages/client/ui-models/src/client/locales.ts b/packages/client/ui-models/src/client/locales.ts index a602453eb1..19463d98fa 100644 --- a/packages/client/ui-models/src/client/locales.ts +++ b/packages/client/ui-models/src/client/locales.ts @@ -61,7 +61,7 @@ export const en = { fetchNeedsBaseUrl: 'Enter the base URL first, then fetch.', fetchEmpty: 'The provider listed no models. Add them by hand.', fetchTitle: 'Choose models to add', - fetchDescription: 'These are the models the provider reports. Choose the ones to add; you can still edit their capacities afterwards.', + fetchDescription: 'These are the models this provider has available. Choose the ones to add.', fetchAdopt: 'Add selected', customAdd: 'Add a custom provider', customTitle: 'Custom provider', @@ -145,7 +145,7 @@ export const zh: typeof en = { fetchNeedsBaseUrl: '请先填写 API 地址,再获取。', fetchEmpty: '该提供方没有列出任何模型,请手动添加。', fetchTitle: '选择要添加的模型', - fetchDescription: '以下是提供方报告的模型。勾选要添加的项,添加后仍可修改其容量。', + fetchDescription: '以下是模型提供方的可用模型,勾选要添加的模型。', fetchAdopt: '添加所选', customAdd: '添加自定义提供方', customTitle: '自定义提供方',