@deepseek-ai/dsh-client-ui-settings-models 0.1.3-alpha.2 → 0.1.5-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +3 -1
- package/README.zh.md +3 -1
- package/lib/client.js +53 -24
- package/lib/types/client/locales.d.ts +1 -0
- package/lib/types/client/store.d.ts +1 -0
- package/package.json +10 -10
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-settings-models/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 283761d17f857c9142a59ee3b333081df84ecd9c
|
|
6
|
+
README.zh.md: 70a5f035cb4ceb03f5d9d593d4ca69a702cbe9b1
|
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ English | [中文](README.zh.md)
|
|
|
27
27
|
|
|
28
28
|
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.
|
|
29
29
|
|
|
30
|
+
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.
|
|
31
|
+
|
|
30
32
|
### API keys
|
|
31
33
|
|
|
32
34
|
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.
|
|
@@ -37,7 +39,7 @@ The collapsed 自定义设置 fold carries the curated extras: `baseURL` for bot
|
|
|
37
39
|
|
|
38
40
|
### Adding and deleting providers
|
|
39
41
|
|
|
40
|
-
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. **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.
|
|
42
|
+
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.
|
|
41
43
|
|
|
42
44
|
### First-run dialogs
|
|
43
45
|
|
package/README.zh.md
CHANGED
|
@@ -27,6 +27,8 @@ kind: "package-reference"
|
|
|
27
27
|
|
|
28
28
|
从设置导航打开 Models 页面,即可看到每个已配置的提供方都有一行。其配置键未在任何位置配置的整分节提供方会渲染为其展开的设置卡片而非一行,但仅限首次运行姿态,且仅持续到用户关闭该卡片为止。每一类卡片各自持有自己的展开状态,因此关掉其中一张绝不会丢弃另一张里的草稿。
|
|
29
29
|
|
|
30
|
+
存在已存储目录错误的提供方仍显示诊断以及编辑、删除入口。添加操作只面向已注册的 settings 命名空间,因此不可用的命名空间不会留下无法打开编辑器的按钮。保存被拒绝时,编辑器保持打开并展示 Host 诊断。
|
|
31
|
+
|
|
30
32
|
### API 密钥
|
|
31
33
|
|
|
32
34
|
编辑卡片上的主字段是单独一个 **API 密钥**输入框——页面从不询问环境变量名。键入的密钥经 `credentials.set` 以**只写**方式存入 profile 的引用之下,profile 没有引用时便派生 `<ROUTE>_API_KEY`,pi-ai profile 会把这次派生记录为 `apiKeyEnv`,因此 `settings.yaml` 从不携带密钥值。为新的 pi-ai 提供方留空密钥会保存一个不带引用的 profile,从而保留提供方原生认证(例如 Bedrock 凭据链或 Vertex ADC)。只有确认引用的凭据已配置时,行才会以绿色实心点标示 API 密钥状态;只有确认具名引用缺失时,才会以红色实心点标示。「应用」成功后会发出本地无障碍状态消息,且绝不回显任何机密内容。
|
|
@@ -37,7 +39,7 @@ kind: "package-reference"
|
|
|
37
39
|
|
|
38
40
|
### 新增与删除提供方
|
|
39
41
|
|
|
40
|
-
「新增」流程是一张承载休眠目录提供方选择框的卡片——裸挂载的 `llm-pi-ai` 在任何路由存在之前就能提供其完整的已安装 catalog。**添加自定义提供方**声明一条 pi-ai 不提供的路由;创建卡片会索要唯一的 **Provider ID
|
|
42
|
+
「新增」流程是一张承载休眠目录提供方选择框的卡片——裸挂载的 `llm-pi-ai` 在任何路由存在之前就能提供其完整的已安装 catalog。**添加自定义提供方**声明一条 pi-ai 不提供的路由;创建卡片会索要唯一的 **Provider ID**、端点、协议与至少一个可唯一识别的模型,因为没有东西能为它们兜底。端点必须是可解析的 HTTP 或 HTTPS URL;localhost、IPv4 与 IPv6 字面地址以及自定义端口仍然有效。语法错误会在字段处阻止询问与创建,请求失败则继续作为独立的提供方错误显示。**获取可用模型**通过 `llm/discoverModels` Remote 查询表单显示的端点,因此新增提供方一次即可完成,而非先保存再返回;回复打开的是可搜索选择器而非直接写入,只有点击**添加所选**才会写入。每个选中候选会在提供方公布相应信息时,把 id、显示名、上下文窗口与最大输出 token 数复制进可编辑行;已经存在的行保留用户调整过的值。搜索会匹配模型 id 与可选显示名称,且不会清除隐藏项的勾选状态。**全选**会加入可见结果,而**取消全选**会清空全部勾选,以免意外采用隐藏结果。只有用户层单独携带某行时,该行才可删除(删除会恢复组合基线),其确认对话框会指名该提供方。
|
|
41
43
|
|
|
42
44
|
### 首次运行弹窗
|
|
43
45
|
|
package/lib/client.js
CHANGED
|
@@ -895,7 +895,8 @@ window.__ModuleLoader__.load({
|
|
|
895
895
|
settingsNs: entry.settingsNs,
|
|
896
896
|
settingsPath: [...entry.settingsPath],
|
|
897
897
|
active: active.has(entry.provider),
|
|
898
|
-
...entry.declared === void 0 ? {} : { declared: entry.declared }
|
|
898
|
+
...entry.declared === void 0 ? {} : { declared: entry.declared },
|
|
899
|
+
...entry.error === void 0 ? {} : { error: entry.error }
|
|
899
900
|
}));
|
|
900
901
|
for (const provider of registered) {
|
|
901
902
|
if (declared.has(provider.id)) continue;
|
|
@@ -1141,6 +1142,14 @@ window.__ModuleLoader__.load({
|
|
|
1141
1142
|
* credential seam with a raw regular expression the user cannot act on.
|
|
1142
1143
|
*/
|
|
1143
1144
|
const ROUTE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
1145
|
+
function isHttpUrl(value) {
|
|
1146
|
+
try {
|
|
1147
|
+
const protocol = new URL(value).protocol;
|
|
1148
|
+
return protocol === "http:" || protocol === "https:";
|
|
1149
|
+
} catch {
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1144
1153
|
/**
|
|
1145
1154
|
* Render the custom-provider creation card.
|
|
1146
1155
|
* @param props - existing routes, protocol choices, wire faces, and copy.
|
|
@@ -1168,11 +1177,13 @@ window.__ModuleLoader__.load({
|
|
|
1168
1177
|
const profileDisabled = disabled || committed;
|
|
1169
1178
|
const routeInvalid = route.length > 0 && !ROUTE_PATTERN.test(route);
|
|
1170
1179
|
const routeTaken = taken.includes(route);
|
|
1180
|
+
const normalizedBaseURL = baseURL.trim();
|
|
1181
|
+
const baseUrlInvalid = baseURL.length > 0 && !isHttpUrl(normalizedBaseURL);
|
|
1171
1182
|
const modelFailure = validateDeepSeekModels(models);
|
|
1172
1183
|
const keyFailure = apiKeyFailure(keyDraft);
|
|
1173
1184
|
const keyValue = keyDraft.trim();
|
|
1174
|
-
const ready = route.length > 0 && !routeInvalid && !routeTaken &&
|
|
1175
|
-
const hint = failure !== void 0 || ready || keyFailure !== void 0 || route.length === 0 || routeInvalid || routeTaken ? void 0 :
|
|
1185
|
+
const ready = route.length > 0 && !routeInvalid && !routeTaken && normalizedBaseURL.length > 0 && !baseUrlInvalid && models.length > 0 && modelFailure === void 0 && keyFailure === void 0;
|
|
1186
|
+
const hint = failure !== void 0 || ready || keyFailure !== void 0 || route.length === 0 || routeInvalid || routeTaken || baseUrlInvalid ? void 0 : normalizedBaseURL.length === 0 ? t("customNeedsBaseUrl") : modelFailure !== void 0 ? `${t("model")} ${String(modelFailure.index + 1)}: ${t(modelFailure.key)}` : t("customNeedsModels");
|
|
1176
1187
|
/** Perform the create, returning a failure message or undefined. */
|
|
1177
1188
|
const createOnce = async () => {
|
|
1178
1189
|
const keyRef = deriveKeyRef(route);
|
|
@@ -1182,7 +1193,7 @@ window.__ModuleLoader__.load({
|
|
|
1182
1193
|
...displayName.length === 0 ? {} : { displayName },
|
|
1183
1194
|
...storesKey ? { apiKeyEnv: keyRef } : {},
|
|
1184
1195
|
api: protocol,
|
|
1185
|
-
baseURL,
|
|
1196
|
+
baseURL: normalizedBaseURL,
|
|
1186
1197
|
models: models.map((model) => ({ ...model }))
|
|
1187
1198
|
};
|
|
1188
1199
|
const written = await operations.writeSettings(NS$1, [{
|
|
@@ -1274,12 +1285,17 @@ window.__ModuleLoader__.load({
|
|
|
1274
1285
|
value: baseURL,
|
|
1275
1286
|
placeholder: t("customBaseUrlPlaceholder"),
|
|
1276
1287
|
"aria-label": t("baseUrl"),
|
|
1288
|
+
"aria-invalid": baseUrlInvalid,
|
|
1277
1289
|
disabled: profileDisabled,
|
|
1278
1290
|
onChange: (event) => {
|
|
1279
1291
|
setBaseURL(event.target.value);
|
|
1280
1292
|
}
|
|
1281
1293
|
})]
|
|
1282
1294
|
}),
|
|
1295
|
+
baseUrlInvalid ? (0, react_jsx_runtime.jsx)("p", {
|
|
1296
|
+
className: ModelsSection_module_css_default["error"],
|
|
1297
|
+
children: t("customBaseUrlInvalid")
|
|
1298
|
+
}) : null,
|
|
1283
1299
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
1284
1300
|
className: ModelsSection_module_css_default["field"],
|
|
1285
1301
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1329,11 +1345,11 @@ window.__ModuleLoader__.load({
|
|
|
1329
1345
|
onChange: setModels,
|
|
1330
1346
|
probe: {
|
|
1331
1347
|
settingsNs: NS$1,
|
|
1332
|
-
baseURL,
|
|
1348
|
+
baseURL: normalizedBaseURL,
|
|
1333
1349
|
api: protocol,
|
|
1334
1350
|
...keyValue.length === 0 ? {} : { apiKey: keyValue }
|
|
1335
1351
|
},
|
|
1336
|
-
probeBlocked: keyFailure === "keyBlank" ? "keyBlankNew" : keyFailure,
|
|
1352
|
+
probeBlocked: baseUrlInvalid ? "customBaseUrlInvalid" : keyFailure === "keyBlank" ? "keyBlankNew" : keyFailure,
|
|
1337
1353
|
operations,
|
|
1338
1354
|
t,
|
|
1339
1355
|
disabled: profileDisabled
|
|
@@ -1928,7 +1944,8 @@ window.__ModuleLoader__.load({
|
|
|
1928
1944
|
};
|
|
1929
1945
|
const anyUsable = state.rows.some(providerUsable);
|
|
1930
1946
|
const configured = state.rows.filter((row) => row.configured);
|
|
1931
|
-
const
|
|
1947
|
+
const configurable = state.rows.filter((row) => state.namespaces.has(row.entry.settingsNs));
|
|
1948
|
+
const addable = configurable.filter((row) => !row.configured);
|
|
1932
1949
|
const addTarget = adding ? editing : void 0;
|
|
1933
1950
|
const addNamespace = addTarget === void 0 ? void 0 : state.namespaces.get(addTarget.settingsNs);
|
|
1934
1951
|
const addRow = addTarget === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === addTarget.provider);
|
|
@@ -1961,23 +1978,32 @@ window.__ModuleLoader__.load({
|
|
|
1961
1978
|
const namespace = state.namespaces.get(target.settingsNs);
|
|
1962
1979
|
/* v8 ignore next -- the join marks a row configured only when its namespace resolved */
|
|
1963
1980
|
if (namespace === void 0) return null;
|
|
1981
|
+
const error = row.entry.error === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
1982
|
+
role: "alert",
|
|
1983
|
+
className: ModelsSection_module_css_default["error"],
|
|
1984
|
+
children: row.entry.error
|
|
1985
|
+
});
|
|
1964
1986
|
if (needsSetup(row, anyUsable) && !dismissedSetup.has(row.entry.provider)) return (0, react_jsx_runtime.jsxs)("li", {
|
|
1965
1987
|
className: ModelsSection_module_css_default["setupCard"],
|
|
1966
|
-
children: [
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1988
|
+
children: [
|
|
1989
|
+
error,
|
|
1990
|
+
renderProviderEditor({
|
|
1991
|
+
target,
|
|
1992
|
+
namespace,
|
|
1993
|
+
schema,
|
|
1994
|
+
operations,
|
|
1995
|
+
t,
|
|
1996
|
+
readOnly: !state.writable,
|
|
1997
|
+
onClose: (changed) => {
|
|
1998
|
+
closeSetup(changed, target);
|
|
1999
|
+
}
|
|
2000
|
+
}),
|
|
2001
|
+
renderSlot("settings.models.provider-card", {
|
|
2002
|
+
provider: row.entry,
|
|
2003
|
+
configured: row.configured,
|
|
2004
|
+
keyConfigured: keyConfiguredOf(row)
|
|
2005
|
+
}, { entryKey: row.entry.settingsNs })
|
|
2006
|
+
]
|
|
1981
2007
|
}, row.entry.provider);
|
|
1982
2008
|
const open = !adding && editing?.provider === row.entry.provider;
|
|
1983
2009
|
const credentialConfigured = row.credential?.configured === true;
|
|
@@ -2037,6 +2063,7 @@ window.__ModuleLoader__.load({
|
|
|
2037
2063
|
}) : null]
|
|
2038
2064
|
})]
|
|
2039
2065
|
}),
|
|
2066
|
+
error,
|
|
2040
2067
|
renderSlot("settings.models.provider-card", {
|
|
2041
2068
|
provider: row.entry,
|
|
2042
2069
|
configured: row.configured,
|
|
@@ -2120,7 +2147,7 @@ window.__ModuleLoader__.load({
|
|
|
2120
2147
|
})
|
|
2121
2148
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
2122
2149
|
className: ModelsSection_module_css_default["addActions"],
|
|
2123
|
-
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
2150
|
+
children: [configurable.length > 0 && (0, react_jsx_runtime.jsxs)("button", {
|
|
2124
2151
|
type: "button",
|
|
2125
2152
|
className: ModelsSection_module_css_default["addButton"],
|
|
2126
2153
|
disabled: addable.length === 0 || !state.writable,
|
|
@@ -2134,7 +2161,7 @@ window.__ModuleLoader__.load({
|
|
|
2134
2161
|
setEditing(targetOf(first));
|
|
2135
2162
|
},
|
|
2136
2163
|
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("add")]
|
|
2137
|
-
}), (0, react_jsx_runtime.jsxs)("button", {
|
|
2164
|
+
}), state.namespaces.has("llm-pi-ai") && (0, react_jsx_runtime.jsxs)("button", {
|
|
2138
2165
|
type: "button",
|
|
2139
2166
|
className: ModelsSection_module_css_default["addButton"],
|
|
2140
2167
|
disabled: protocols.length === 0 || !state.writable,
|
|
@@ -2703,6 +2730,7 @@ window.__ModuleLoader__.load({
|
|
|
2703
2730
|
customApi: "API protocol",
|
|
2704
2731
|
customApiUnset: "Not selected",
|
|
2705
2732
|
customNeedsBaseUrl: "A custom provider needs a base URL.",
|
|
2733
|
+
customBaseUrlInvalid: "Enter a valid HTTP or HTTPS URL.",
|
|
2706
2734
|
customNeedsModels: "A custom provider needs at least one model.",
|
|
2707
2735
|
customBaseUrlPlaceholder: "https://gateway.example/v1",
|
|
2708
2736
|
settingsPathUnresolvable: "unresolvable settings path",
|
|
@@ -2805,6 +2833,7 @@ window.__ModuleLoader__.load({
|
|
|
2805
2833
|
customApi: "API 协议",
|
|
2806
2834
|
customApiUnset: "未选择",
|
|
2807
2835
|
customNeedsBaseUrl: "自定义提供方需要填写 API 地址。",
|
|
2836
|
+
customBaseUrlInvalid: "请输入有效的 HTTP 或 HTTPS 地址。",
|
|
2808
2837
|
customNeedsModels: "自定义提供方至少需要一个模型。",
|
|
2809
2838
|
customBaseUrlPlaceholder: "https://gateway.example/v1",
|
|
2810
2839
|
settingsPathUnresolvable: "无法解析设置路径",
|
|
@@ -19,6 +19,7 @@ export interface ProviderDirectoryEntry {
|
|
|
19
19
|
readonly settingsPath: readonly string[];
|
|
20
20
|
readonly active: boolean;
|
|
21
21
|
readonly declared?: boolean;
|
|
22
|
+
readonly error?: string;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Join declared configurable providers with the currently registered routes.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-settings-models",
|
|
3
3
|
"description": "Models settings and shared product-onboarding dialogs over existing settings and credential joins",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -42,16 +42,16 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/react": "~18.3.1",
|
|
44
44
|
"react": "^18.2.0",
|
|
45
|
-
"@deepseek-ai/dsh-
|
|
46
|
-
"@deepseek-ai/dsh-client-
|
|
47
|
-
"@deepseek-ai/dsh-
|
|
48
|
-
"@deepseek-ai/dsh-client-
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.3-alpha.2",
|
|
45
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.5-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.5-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2",
|
|
52
51
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
53
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.
|
|
54
|
-
"@deepseek-ai/dsh-util-values": "^0.1.
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-util-values": "^0.1.5-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-store": "^0.1.5-alpha.2"
|
|
55
55
|
},
|
|
56
56
|
"files": [
|
|
57
57
|
"lib/index.js",
|