@dsh-plus/llm-pi 0.1.41 → 0.1.43
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/lib/client.js +1528 -767
- package/lib/index.d.ts +4 -8
- package/lib/index.js +943 -391
- package/package.json +35 -36
- package/src/catalog/browse.ts +297 -0
- package/src/catalog/builtin.ts +183 -35
- package/src/client/api.ts +96 -33
- package/src/client/card.tsx +183 -133
- package/src/client/client.ts +4 -4
- package/src/client/constants.ts +25 -5
- package/src/client/draft.ts +34 -13
- package/src/client/fields.tsx +18 -3
- package/src/client/i18n.ts +153 -49
- package/src/client/root-fields.tsx +64 -52
- package/src/client/route-fit.ts +117 -0
- package/src/client/styles.ts +40 -10
- package/src/client/views/catalog-browser.tsx +376 -0
- package/src/client/views/catalog-row.tsx +147 -0
- package/src/client/views/compat.tsx +31 -3
- package/src/client/views/models.tsx +189 -193
- package/src/client/views/provider-fields.tsx +2 -2
- package/src/client/views/providers.tsx +51 -11
- package/src/compat.ts +43 -19
- package/src/config-api.ts +65 -37
- package/src/config.ts +20 -28
- package/src/inherit.ts +15 -31
- package/src/kit-meta.ts +156 -0
- package/src/ns.ts +2 -2
- package/src/{compat-gates.ts → official-surface.ts} +60 -16
- package/src/profiles.ts +52 -13
- package/src/resolve-dsh.ts +291 -72
- package/src/service.ts +209 -90
- package/src/catalog/models-dev.ts +0 -261
package/src/client/draft.ts
CHANGED
|
@@ -69,9 +69,6 @@ export interface ProviderDraft {
|
|
|
69
69
|
|
|
70
70
|
export interface Draft {
|
|
71
71
|
enabled: boolean
|
|
72
|
-
catalogUrl: string
|
|
73
|
-
catalogRefreshHours: string
|
|
74
|
-
catalogProxy: string
|
|
75
72
|
providers: Record<string, ProviderDraft>
|
|
76
73
|
}
|
|
77
74
|
|
|
@@ -355,9 +352,6 @@ export function emptyModelDraft(): ModelDraft {
|
|
|
355
352
|
export function draftFromValue(value: ConfigValue): Draft {
|
|
356
353
|
return {
|
|
357
354
|
enabled: value.enabled,
|
|
358
|
-
catalogUrl: value.catalogUrl,
|
|
359
|
-
catalogRefreshHours: String(value.catalogRefreshHours),
|
|
360
|
-
catalogProxy: value.catalogProxy,
|
|
361
355
|
providers: Object.fromEntries(
|
|
362
356
|
Object.entries(value.providers).map(([route, provider]) => [
|
|
363
357
|
route,
|
|
@@ -369,17 +363,44 @@ export function draftFromValue(value: ConfigValue): Draft {
|
|
|
369
363
|
|
|
370
364
|
/** 提交补丁:完整配置对象,providers 全量替换;空值一律剔除。 */
|
|
371
365
|
export function toPatch(draft: Draft): ConfigPatch {
|
|
372
|
-
const refreshHours = toNum(draft.catalogRefreshHours)
|
|
373
366
|
return {
|
|
374
367
|
enabled: draft.enabled,
|
|
375
|
-
catalogUrl: draft.catalogUrl.trim(),
|
|
376
|
-
// 保存动作在文本非法时不可用(card.tsx 的 invalid 门控),故写入路径必为合法数字;
|
|
377
|
-
// dirty 比较路径可能拿到非法文本的 undefined(JSON.stringify 时同键被丢弃),
|
|
378
|
-
// 此处仅把"保存前已校验"的运行期事实带到类型层,取值不变。
|
|
379
|
-
catalogRefreshHours: refreshHours as unknown as number,
|
|
380
|
-
catalogProxy: draft.catalogProxy.trim(),
|
|
381
368
|
providers: Object.fromEntries(
|
|
382
369
|
Object.entries(draft.providers).map(([route, provider]) => [route, providerToWire(provider)]),
|
|
383
370
|
),
|
|
384
371
|
}
|
|
385
372
|
}
|
|
373
|
+
|
|
374
|
+
/** 目录一键添加的结果:added=false 表示该 route 已有同 id 条目(原样返回草稿)。 */
|
|
375
|
+
export interface AddModelResult {
|
|
376
|
+
draft: Draft
|
|
377
|
+
added: boolean
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* 把一个内置目录模型加进目标 route:只写 `id` + `extends`(**不预填**
|
|
382
|
+
* name/contextWindow/maxTokens——继承才能跟随 pi-ai 目录升级,预填会把值冻住)。
|
|
383
|
+
* 同 id 已存在时原样返回(added=false),不产生重复条目。
|
|
384
|
+
*/
|
|
385
|
+
export function addModelEntry(
|
|
386
|
+
draft: Draft,
|
|
387
|
+
route: string,
|
|
388
|
+
model: { id: string; path: string },
|
|
389
|
+
): AddModelResult {
|
|
390
|
+
const provider = draft.providers[route]
|
|
391
|
+
if (provider === undefined) return { draft, added: false }
|
|
392
|
+
if (provider.models.some((entry) => entry.id.trim() === model.id)) {
|
|
393
|
+
return { draft, added: false }
|
|
394
|
+
}
|
|
395
|
+
const entry: ModelDraft = { ...emptyModelDraft(), id: model.id, extends: model.path }
|
|
396
|
+
return {
|
|
397
|
+
draft: {
|
|
398
|
+
...draft,
|
|
399
|
+
providers: {
|
|
400
|
+
...draft.providers,
|
|
401
|
+
[route]: { ...provider, models: [...provider.models, entry] },
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
added: true,
|
|
405
|
+
}
|
|
406
|
+
}
|
package/src/client/fields.tsx
CHANGED
|
@@ -13,18 +13,30 @@ import type { HeaderPair } from './draft.ts'
|
|
|
13
13
|
/**
|
|
14
14
|
* 可折叠小节:复杂项(compat/retryPolicy 等)默认折叠,避免卡片过长;
|
|
15
15
|
* 基础项(baseURL 等)保持展开。标题按钮切换展开态。
|
|
16
|
+
* 传 `open` 即为**受控**(由外部状态决定展开,如卡片级"全部收起"与
|
|
17
|
+
* 「从内置目录添加」联动展开目标 route);不传则内部自管。
|
|
16
18
|
*/
|
|
17
19
|
export interface CollapseSectionProps {
|
|
18
20
|
id: string
|
|
19
21
|
title: string
|
|
20
|
-
/** 默认展开态;复杂项传 false
|
|
22
|
+
/** 默认展开态;复杂项传 false(默认折叠)。受控模式下仅作初值语义的说明。 */
|
|
21
23
|
defaultOpen: boolean
|
|
24
|
+
/** 受控展开态;传了就完全由外部决定。 */
|
|
25
|
+
open?: boolean
|
|
26
|
+
onToggle?(): void
|
|
27
|
+
/** 标题右侧的计数/状态副标题(如 compat「已设 3/19」)。 */
|
|
28
|
+
meta?: string
|
|
22
29
|
disabled?: boolean
|
|
23
30
|
children: ReactNode
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
export function CollapseSection(props: CollapseSectionProps): ReactElement {
|
|
27
|
-
const [
|
|
34
|
+
const [innerOpen, setInnerOpen] = useState(props.defaultOpen)
|
|
35
|
+
const open = props.open ?? innerOpen
|
|
36
|
+
const toggle = (): void => {
|
|
37
|
+
if (props.onToggle !== undefined) props.onToggle()
|
|
38
|
+
else setInnerOpen((value) => !value)
|
|
39
|
+
}
|
|
28
40
|
return (
|
|
29
41
|
<div className="lpc-collapse">
|
|
30
42
|
<button
|
|
@@ -32,10 +44,13 @@ export function CollapseSection(props: CollapseSectionProps): ReactElement {
|
|
|
32
44
|
className="lpc-collapseHead"
|
|
33
45
|
id={props.id}
|
|
34
46
|
aria-expanded={open}
|
|
35
|
-
onClick={
|
|
47
|
+
onClick={toggle}
|
|
36
48
|
>
|
|
37
49
|
<ChevronDownIcon className={`lpc-chevron${open ? ' lpc-chevronOpen' : ''}`} />
|
|
38
50
|
<span className="lpc-collapseTitle">{props.title}</span>
|
|
51
|
+
{props.meta !== undefined && props.meta !== '' ? (
|
|
52
|
+
<span className="lpc-collapseMeta">{props.meta}</span>
|
|
53
|
+
) : null}
|
|
39
54
|
</button>
|
|
40
55
|
{open ? <div className="lpc-collapseBody">{props.children}</div> : null}
|
|
41
56
|
</div>
|
package/src/client/i18n.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 配置卡片文案(zh/en)。经 ctx.locale.register 注册、bind 取用,与官方卡片同机制。
|
|
3
3
|
* 公共键(save/discard/unsaved 等)来自 shared 的 common 字典,本文件只维护业务键。
|
|
4
|
+
* 服务端推导出来的诊断/提示(compat problem、版本区间提示、降级诊断)按原文展示,
|
|
5
|
+
* 不做二次翻译——它们是事实描述,且与日志同一份文本。
|
|
4
6
|
* @module llm-pi/client/i18n
|
|
5
7
|
*/
|
|
6
8
|
import { commonEn, commonZh, mergeDict } from '@dsh-plus/shared/client'
|
|
@@ -9,29 +11,74 @@ export const NS = 'dsh-plus-llm-pi'
|
|
|
9
11
|
|
|
10
12
|
const ownZh = {
|
|
11
13
|
title: 'LLM 路由(llm-pi)',
|
|
12
|
-
description:
|
|
13
|
-
|
|
14
|
+
description:
|
|
15
|
+
'自定义 LLM 路由:协议、compat、模型继承;模型目录唯一来自 pi-ai 内置目录(随已装 dsh 自动跟随)。',
|
|
16
|
+
summaryLine:
|
|
17
|
+
'自定义 LLM 路由:协议、compat、模型继承;模型目录唯一来自 pi-ai 内置目录(随已装 dsh 自动跟随)。',
|
|
14
18
|
enabled: '启用插件',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
runtimeGroup: '运行期套件',
|
|
20
|
+
runtimeSource: '来源',
|
|
21
|
+
runtimeRoot: '安装树',
|
|
22
|
+
runtimeVersions: '生效版本',
|
|
23
|
+
runtimeCatalog: '内置模型目录',
|
|
24
|
+
runtimeProtocols: '可服务协议',
|
|
25
|
+
runtimeCompat: 'compat 门控表',
|
|
26
|
+
runtimeDiagnostics: '诊断',
|
|
27
|
+
runtimeVersionUnknown: '未知',
|
|
28
|
+
runtimeCatalogUnit: '个 provider /',
|
|
29
|
+
runtimeCatalogModels: '个模型',
|
|
30
|
+
runtimeCatalogGenerated: '数据生成于',
|
|
31
|
+
runtimeNotice: '提示',
|
|
32
|
+
runtimeDerived: '现场推导',
|
|
33
|
+
runtimeFallback: '回退快照',
|
|
34
|
+
browserGroup: '内置模型目录',
|
|
35
|
+
browserHint:
|
|
36
|
+
'浏览 pi-ai 内置目录(随已装 dsh 的 pi-ai 版本自动更新):模糊搜索、按供应商/协议/能力筛选;复制路径 id,或一键把「模型 id + 继承源」加到某个 route。',
|
|
37
|
+
browserSearch: '模糊搜索',
|
|
38
|
+
browserSearchPlaceholder: '模型 id / 显示名 / provider,多词用空格',
|
|
39
|
+
browserProvider: '供应商',
|
|
40
|
+
browserApi: '协议',
|
|
41
|
+
browserAll: '全部',
|
|
42
|
+
browserOnlyServable: '仅本插件可服务',
|
|
43
|
+
browserReasoning: '推理',
|
|
44
|
+
browserImage: '图片输入',
|
|
45
|
+
browserTarget: '目标 route',
|
|
46
|
+
browserNoTarget:
|
|
47
|
+
'还没有可添加的 route:先在上方新增一个 route(adapter: deepseek 的路由不适用 pi-ai 继承)。',
|
|
48
|
+
browserMatched: '命中',
|
|
49
|
+
browserHiddenServable: '另有协议不受支持的模型',
|
|
50
|
+
browserHiddenUnit: '个已隐藏',
|
|
51
|
+
browserLoadMore: '加载更多',
|
|
52
|
+
browserLoading: '目录加载中…',
|
|
53
|
+
browserEmpty: '没有命中的模型。',
|
|
54
|
+
browserFailed: '目录加载失败:',
|
|
55
|
+
browserAdd: '添加',
|
|
56
|
+
browserAdded: '已添加',
|
|
57
|
+
browserCopy: '复制路径 id',
|
|
58
|
+
browserCopied: '已复制',
|
|
59
|
+
browserDetails: '参数明细',
|
|
60
|
+
browserCompat: 'compat',
|
|
61
|
+
browserExtra: '其余参数',
|
|
62
|
+
browserThinkMap: '思考档位',
|
|
63
|
+
browserInputText: '文本',
|
|
64
|
+
browserInputImage: '图片',
|
|
65
|
+
browserContextShort: '上下文',
|
|
66
|
+
browserMaxShort: '输出',
|
|
67
|
+
browserAddedTo: '已添加到 route',
|
|
68
|
+
browserTargetProtocol: '当前协议',
|
|
69
|
+
browserNoProtocol: '协议待定',
|
|
70
|
+
blockUnsupported: '该协议本插件无法服务(不可添加)',
|
|
71
|
+
blockDuplicate: '该 route 已有同 id 模型',
|
|
72
|
+
blockConflict: '与目标 route 的协议不一致',
|
|
73
|
+
blockMixed: '目标 route 已混用多个协议,请先修正',
|
|
74
|
+
blockNoRoute: '目标 route 不存在',
|
|
30
75
|
advancedGroup: '高级设置(retryPolicy / compat)',
|
|
31
76
|
extraFields: '其余字段(JSON)',
|
|
32
77
|
extraFieldsHint:
|
|
33
78
|
'表单未覆盖的 wire 字段原样往返:如 adapter=deepseek 的 thinking / reasoningEffort、Files API 与图片配额等。对象整体合并进该 route(模型级同 key 覆盖 route 级),非法 JSON 不会提交。',
|
|
34
79
|
providersGroup: 'Provider 路由',
|
|
80
|
+
providersCollapseAll: '全部收起',
|
|
81
|
+
providersExpandAll: '全部展开',
|
|
35
82
|
addRoute: '新增 route',
|
|
36
83
|
addRoutePlaceholder: '新 route 键名(如 my-llm)',
|
|
37
84
|
routeEmpty: 'route 键名不能为空。',
|
|
@@ -39,7 +86,8 @@ const ownZh = {
|
|
|
39
86
|
deleteRoute: '删除 route',
|
|
40
87
|
providerFields: '基础字段',
|
|
41
88
|
extends: '继承内置 provider',
|
|
42
|
-
extendsHint:
|
|
89
|
+
extendsHint:
|
|
90
|
+
'如 openai、anthropic(候选见「内置模型目录」);提供 api/baseURL 默认值与模型查找源。',
|
|
43
91
|
displayName: '显示名',
|
|
44
92
|
api: '线协议',
|
|
45
93
|
baseURL: '端点 URL',
|
|
@@ -70,55 +118,104 @@ const ownZh = {
|
|
|
70
118
|
compatGroup: 'Compat 覆盖',
|
|
71
119
|
compatApiHint: 'api 未设置时暂按 openai-completions 字段组渲染;保存时后端按实际协议校验。',
|
|
72
120
|
compatUnset: '未设置',
|
|
121
|
+
compatSetCount: '已设',
|
|
122
|
+
compatFilter: '过滤字段',
|
|
73
123
|
modelsGroup: '模型目录',
|
|
74
124
|
addModel: '添加模型',
|
|
75
|
-
|
|
125
|
+
addFromCatalog: '从内置目录添加',
|
|
126
|
+
modelsCollapseAll: '全部收起',
|
|
127
|
+
modelsExpandAll: '全部展开',
|
|
128
|
+
modelsFilter: '过滤模型',
|
|
129
|
+
modelsFilterPlaceholder: '按 id / 继承源筛选',
|
|
130
|
+
modelsHidden: '已按筛选隐藏',
|
|
76
131
|
modelRow: '模型',
|
|
132
|
+
modelUntitled: '(未填 id)',
|
|
133
|
+
deleteModel: '删除',
|
|
77
134
|
modelId: '模型 id',
|
|
78
135
|
modelIdHint: '发送给 provider 的标识,必填。',
|
|
79
136
|
modelIdRequired: '模型 id 必填。',
|
|
80
137
|
modelExtends: '继承源',
|
|
81
|
-
modelExtendsHint: '"provider/model" 或裸 model id
|
|
138
|
+
modelExtendsHint: '"provider/model" 或裸 model id;候选见「内置模型目录」。',
|
|
82
139
|
modelName: '显示名',
|
|
83
140
|
contextWindow: '上下文容量',
|
|
84
141
|
maxTokens: '输出上限',
|
|
85
142
|
input: '输入模态',
|
|
86
143
|
reasoningEfforts: 'reasoningEfforts',
|
|
87
144
|
nonReasoning: '非推理模型',
|
|
88
|
-
catalogSource: '候选来源',
|
|
89
|
-
catalogProvider: '候选 provider',
|
|
90
|
-
catalogLoading: '目录加载中…',
|
|
91
|
-
catalogFailed: '目录加载失败。',
|
|
92
145
|
saveOk: '已保存。',
|
|
93
146
|
saveFailed: '保存失败:',
|
|
94
147
|
} as const
|
|
95
148
|
|
|
96
149
|
const ownEn = {
|
|
97
150
|
title: 'LLM routes (llm-pi)',
|
|
98
|
-
description:
|
|
99
|
-
|
|
151
|
+
description:
|
|
152
|
+
'Custom LLM routes: protocol, compat, model inheritance; the model catalog comes solely from the pi-ai built-in catalog (following the installed dsh).',
|
|
153
|
+
summaryLine:
|
|
154
|
+
'Custom LLM routes: protocol, compat, model inheritance; the model catalog comes solely from the pi-ai built-in catalog.',
|
|
100
155
|
enabled: 'Enable plugin',
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
156
|
+
runtimeGroup: 'Runtime kit',
|
|
157
|
+
runtimeSource: 'Source',
|
|
158
|
+
runtimeRoot: 'Install tree',
|
|
159
|
+
runtimeVersions: 'Effective versions',
|
|
160
|
+
runtimeCatalog: 'Built-in catalog',
|
|
161
|
+
runtimeProtocols: 'Servable protocols',
|
|
162
|
+
runtimeCompat: 'compat gates',
|
|
163
|
+
runtimeDiagnostics: 'Diagnostics',
|
|
164
|
+
runtimeVersionUnknown: 'unknown',
|
|
165
|
+
runtimeCatalogUnit: 'providers /',
|
|
166
|
+
runtimeCatalogModels: 'models',
|
|
167
|
+
runtimeCatalogGenerated: 'data generated at',
|
|
168
|
+
runtimeNotice: 'Notice',
|
|
169
|
+
runtimeDerived: 'derived live',
|
|
170
|
+
runtimeFallback: 'fallback snapshot',
|
|
171
|
+
browserGroup: 'Built-in model catalog',
|
|
172
|
+
browserHint:
|
|
173
|
+
'Browse the pi-ai built-in catalog (follows the installed dsh): fuzzy search, filter by provider/protocol/capability; copy the path id, or add "model id + inheritance source" to a route in one click.',
|
|
174
|
+
browserSearch: 'Fuzzy search',
|
|
175
|
+
browserSearchPlaceholder: 'model id / display name / provider; space-separated terms',
|
|
176
|
+
browserProvider: 'Provider',
|
|
177
|
+
browserApi: 'Protocol',
|
|
178
|
+
browserAll: 'All',
|
|
179
|
+
browserOnlyServable: 'Servable only',
|
|
180
|
+
browserReasoning: 'Reasoning',
|
|
181
|
+
browserImage: 'Image input',
|
|
182
|
+
browserTarget: 'Target route',
|
|
183
|
+
browserNoTarget:
|
|
184
|
+
'No route to add to yet: create one above (adapter: deepseek routes do not use pi-ai inheritance).',
|
|
185
|
+
browserMatched: 'Matched',
|
|
186
|
+
browserHiddenServable: 'Unsupported-protocol models',
|
|
187
|
+
browserHiddenUnit: 'hidden',
|
|
188
|
+
browserLoadMore: 'Load more',
|
|
189
|
+
browserLoading: 'Loading catalog…',
|
|
190
|
+
browserEmpty: 'No matching model.',
|
|
191
|
+
browserFailed: 'Catalog request failed: ',
|
|
192
|
+
browserAdd: 'Add',
|
|
193
|
+
browserAdded: 'Added',
|
|
194
|
+
browserCopy: 'Copy path id',
|
|
195
|
+
browserCopied: 'Copied',
|
|
196
|
+
browserDetails: 'Parameters',
|
|
197
|
+
browserCompat: 'compat',
|
|
198
|
+
browserExtra: 'Other parameters',
|
|
199
|
+
browserThinkMap: 'Thinking levels',
|
|
200
|
+
browserInputText: 'text',
|
|
201
|
+
browserInputImage: 'image',
|
|
202
|
+
browserContextShort: 'context',
|
|
203
|
+
browserMaxShort: 'output',
|
|
204
|
+
browserAddedTo: 'Added to route',
|
|
205
|
+
browserTargetProtocol: 'protocol',
|
|
206
|
+
browserNoProtocol: 'protocol undecided',
|
|
207
|
+
blockUnsupported: 'This protocol is not servable by this plugin',
|
|
208
|
+
blockDuplicate: 'This route already has a model with the same id',
|
|
209
|
+
blockConflict: 'Protocol differs from the target route',
|
|
210
|
+
blockMixed: 'The target route already mixes protocols — fix it first',
|
|
211
|
+
blockNoRoute: 'Target route does not exist',
|
|
117
212
|
advancedGroup: 'Advanced (retryPolicy / compat)',
|
|
118
213
|
extraFields: 'Other fields (JSON)',
|
|
119
214
|
extraFieldsHint:
|
|
120
215
|
'Wire fields the form does not cover, round-tripped verbatim: e.g. thinking / reasoningEffort and the Files API & image quotas for adapter=deepseek. The object merges into this route (model-level keys win); invalid JSON is not submitted.',
|
|
121
216
|
providersGroup: 'Provider routes',
|
|
217
|
+
providersCollapseAll: 'Collapse all',
|
|
218
|
+
providersExpandAll: 'Expand all',
|
|
122
219
|
addRoute: 'Add route',
|
|
123
220
|
addRoutePlaceholder: 'New route key (e.g. my-llm)',
|
|
124
221
|
routeEmpty: 'Route key must not be empty.',
|
|
@@ -126,7 +223,8 @@ const ownEn = {
|
|
|
126
223
|
deleteRoute: 'Delete route',
|
|
127
224
|
providerFields: 'Basic fields',
|
|
128
225
|
extends: 'Inherit built-in provider',
|
|
129
|
-
extendsHint:
|
|
226
|
+
extendsHint:
|
|
227
|
+
'e.g. openai, anthropic (see the built-in catalog below); provides api/baseURL defaults and the model lookup source.',
|
|
130
228
|
displayName: 'Display name',
|
|
131
229
|
api: 'Wire protocol',
|
|
132
230
|
baseURL: 'Base URL',
|
|
@@ -158,25 +256,31 @@ const ownEn = {
|
|
|
158
256
|
compatApiHint:
|
|
159
257
|
'When api is unset, fields render per openai-completions; the backend validates per the effective protocol.',
|
|
160
258
|
compatUnset: 'Unset',
|
|
259
|
+
compatSetCount: 'set',
|
|
260
|
+
compatFilter: 'Filter fields',
|
|
161
261
|
modelsGroup: 'Model catalog',
|
|
162
262
|
addModel: 'Add model',
|
|
163
|
-
|
|
263
|
+
addFromCatalog: 'Add from catalog',
|
|
264
|
+
modelsCollapseAll: 'Collapse all',
|
|
265
|
+
modelsExpandAll: 'Expand all',
|
|
266
|
+
modelsFilter: 'Filter models',
|
|
267
|
+
modelsFilterPlaceholder: 'filter by id / inheritance source',
|
|
268
|
+
modelsHidden: 'hidden by filter',
|
|
164
269
|
modelRow: 'Model',
|
|
270
|
+
modelUntitled: '(no id yet)',
|
|
271
|
+
deleteModel: 'Delete',
|
|
165
272
|
modelId: 'Model id',
|
|
166
273
|
modelIdHint: 'The identifier sent to the provider; required.',
|
|
167
274
|
modelIdRequired: 'Model id is required.',
|
|
168
275
|
modelExtends: 'Inherits from',
|
|
169
|
-
modelExtendsHint:
|
|
276
|
+
modelExtendsHint:
|
|
277
|
+
'"provider/model" or a bare model id; candidates in the built-in catalog above.',
|
|
170
278
|
modelName: 'Display name',
|
|
171
279
|
contextWindow: 'Context window',
|
|
172
280
|
maxTokens: 'Max tokens',
|
|
173
281
|
input: 'Input modalities',
|
|
174
282
|
reasoningEfforts: 'reasoningEfforts',
|
|
175
283
|
nonReasoning: 'Non-reasoning model',
|
|
176
|
-
catalogSource: 'Candidate source',
|
|
177
|
-
catalogProvider: 'Candidate provider',
|
|
178
|
-
catalogLoading: 'Loading catalog…',
|
|
179
|
-
catalogFailed: 'Failed to load catalog.',
|
|
180
284
|
saveOk: 'Saved.',
|
|
181
285
|
saveFailed: 'Save failed: ',
|
|
182
286
|
} as const
|
|
@@ -1,36 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 卡片根字段组:总开关 + 运行期状态行。
|
|
3
|
+
*
|
|
4
|
+
* 状态行回答的是"我现在到底在跑哪一份 pi-ai"——插件按文件路径动态加载已装 dsh 树
|
|
5
|
+
* 里的套件(见 resolve-dsh.ts),manifest 上写的版本**不是**生效版本。这里显示
|
|
6
|
+
* 生效版本、安装树、目录规模与数据日期、可服务协议、compat 表来源,以及逐项降级
|
|
7
|
+
* 诊断;版本超出验证区间时给出提示(提示而非阻断)。
|
|
4
8
|
* @module llm-pi/client/root-fields
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
|
-
import { CheckRow
|
|
11
|
+
import { CheckRow } from '@dsh-plus/shared/client'
|
|
8
12
|
import type { ReactElement } from 'react'
|
|
9
13
|
|
|
10
|
-
import type {
|
|
11
|
-
import {
|
|
14
|
+
import type { WireKitInfo } from './api.ts'
|
|
15
|
+
import type { Draft } from './draft.ts'
|
|
12
16
|
import type { Translate } from './i18n.ts'
|
|
13
17
|
|
|
14
|
-
/**
|
|
15
|
-
function
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/** 时间戳 → 本地日期(秒级精度足够)。 */
|
|
19
|
+
function formatTime(ms: number | undefined, fallback: string): string {
|
|
20
|
+
if (ms === undefined || !Number.isFinite(ms)) return fallback
|
|
21
|
+
return new Date(ms).toLocaleString()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 生效版本三连(缺失显示"未知")。 */
|
|
25
|
+
function versionsText(kit: WireKitInfo, t: Translate): string {
|
|
26
|
+
const unknown = t('runtimeVersionUnknown')
|
|
27
|
+
return [
|
|
28
|
+
`pi-ai ${kit.versions.piAi ?? unknown}`,
|
|
29
|
+
`dsh-llm-pi-ai ${kit.versions.piAiAdapter ?? unknown}`,
|
|
30
|
+
...(kit.versions.dsh === undefined ? [] : [`dsh ${kit.versions.dsh}`]),
|
|
31
|
+
].join(' · ')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 来源标签:现场推导 / 回退快照。 */
|
|
35
|
+
function sourceLabel(t: Translate, source: string): string {
|
|
36
|
+
return source === 'official' ? t('runtimeDerived') : t('runtimeFallback')
|
|
20
37
|
}
|
|
21
38
|
|
|
22
39
|
export interface RootFieldsProps {
|
|
23
40
|
t: Translate
|
|
24
41
|
draft: Draft
|
|
25
42
|
disabled: boolean
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
kit: WireKitInfo | null
|
|
44
|
+
/** 目录索引拉取失败原因(失败时状态行显式提示,其余字段照常可编辑)。 */
|
|
45
|
+
kitError: string
|
|
28
46
|
/** 根字段编辑(合并式写入并清空状态行)。 */
|
|
29
47
|
onEdit(patch: Partial<Draft>): void
|
|
30
48
|
}
|
|
31
49
|
|
|
32
50
|
export function RootFields(props: RootFieldsProps): ReactElement {
|
|
33
|
-
const { t, draft, disabled, onEdit } = props
|
|
51
|
+
const { t, draft, disabled, kit, onEdit } = props
|
|
34
52
|
return (
|
|
35
53
|
<>
|
|
36
54
|
<CheckRow
|
|
@@ -41,44 +59,38 @@ export function RootFields(props: RootFieldsProps): ReactElement {
|
|
|
41
59
|
disabled={disabled}
|
|
42
60
|
onEdit={(value) => onEdit({ enabled: value })}
|
|
43
61
|
/>
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</p>
|
|
77
|
-
<div className="lpc-statusRow">
|
|
78
|
-
<span>
|
|
79
|
-
{t('modelsDevStatus')}:{modelsDevText(props.modelsDevStatus, t)}
|
|
80
|
-
</span>
|
|
81
|
-
</div>
|
|
62
|
+
<p className="lpc-groupLabel">{t('runtimeGroup')}</p>
|
|
63
|
+
{props.kitError !== '' ? (
|
|
64
|
+
<p className="lpc-invalid">{`${t('browserFailed')}${props.kitError}`}</p>
|
|
65
|
+
) : null}
|
|
66
|
+
{kit === null ? (
|
|
67
|
+
<p className="lpc-hint">{t('browserLoading')}</p>
|
|
68
|
+
) : (
|
|
69
|
+
<>
|
|
70
|
+
<p className="lpc-statusRow">
|
|
71
|
+
{`${t('runtimeSource')}:${kit.source}${kit.root === undefined ? '' : ` · ${t('runtimeRoot')} ${kit.root}`}`}
|
|
72
|
+
</p>
|
|
73
|
+
<p className="lpc-statusRow">{`${t('runtimeVersions')}:${versionsText(kit, t)}`}</p>
|
|
74
|
+
<p className="lpc-statusRow">
|
|
75
|
+
{`${t('runtimeCatalog')}:${kit.catalog.providers} ${t('runtimeCatalogUnit')} ${kit.catalog.models} ${t('runtimeCatalogModels')} · ${t('runtimeCatalogGenerated')} ${formatTime(kit.catalog.generatedAt, t('runtimeVersionUnknown'))}`}
|
|
76
|
+
</p>
|
|
77
|
+
<p className="lpc-statusRow">
|
|
78
|
+
{`${t('runtimeProtocols')}:${kit.protocols.join(' / ')}(${sourceLabel(t, kit.protocolSource)})`}
|
|
79
|
+
</p>
|
|
80
|
+
<p className="lpc-statusRow">
|
|
81
|
+
{`${t('runtimeCompat')}:${sourceLabel(t, kit.compatSource)}`}
|
|
82
|
+
</p>
|
|
83
|
+
{kit.versionNotice !== undefined ? (
|
|
84
|
+
<p className="lpc-invalid">{`${t('runtimeNotice')}:${kit.versionNotice}`}</p>
|
|
85
|
+
) : null}
|
|
86
|
+
{kit.diagnostics.length > 0 ? (
|
|
87
|
+
<>
|
|
88
|
+
<p className="lpc-catDetailLabel">{t('runtimeDiagnostics')}</p>
|
|
89
|
+
<pre className="lpc-catJson">{kit.diagnostics.join('\n')}</pre>
|
|
90
|
+
</>
|
|
91
|
+
) : null}
|
|
92
|
+
</>
|
|
93
|
+
)}
|
|
82
94
|
</>
|
|
83
95
|
)
|
|
84
96
|
}
|