@dsh-plus/llm-pi 0.1.24 → 0.1.26
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 +4 -2
- package/lib/index.d.ts +6 -1
- package/lib/index.js +14 -4
- package/package.json +30 -30
- package/src/catalog/builtin.ts +10 -3
- package/src/catalog/official.ts +2 -1
- package/src/client/draft.ts +9 -5
- package/src/client/fields.tsx +2 -2
- package/src/client/views/compat.tsx +2 -0
- package/src/config-api.ts +1 -0
- package/src/config.ts +19 -3
- package/src/discovery.ts +6 -4
- package/src/profiles.ts +4 -1
- package/src/resolve-dsh.ts +9 -2
- package/src/service.ts +3 -3
package/lib/client.js
CHANGED
|
@@ -516,7 +516,7 @@ ${extra}
|
|
|
516
516
|
"retryPolicy",
|
|
517
517
|
"models"
|
|
518
518
|
]);
|
|
519
|
-
/** 摘出 wire 对象里卡片未认识的字段(undefined
|
|
519
|
+
/** 摘出 wire 对象里卡片未认识的字段(undefined 值不保留;入参只要求是普通对象)。 */
|
|
520
520
|
function extraOf(wire, known) {
|
|
521
521
|
const out = {};
|
|
522
522
|
for (const [key, value] of Object.entries(wire)) if (!known.has(key) && value !== void 0) out[key] = value;
|
|
@@ -666,10 +666,11 @@ ${extra}
|
|
|
666
666
|
}
|
|
667
667
|
/** 提交补丁:完整配置对象,providers 全量替换;空值一律剔除。 */
|
|
668
668
|
function toPatch(draft) {
|
|
669
|
+
const refreshHours = toNum(draft.catalogRefreshHours);
|
|
669
670
|
return {
|
|
670
671
|
enabled: draft.enabled,
|
|
671
672
|
catalogUrl: draft.catalogUrl.trim(),
|
|
672
|
-
catalogRefreshHours:
|
|
673
|
+
catalogRefreshHours: refreshHours,
|
|
673
674
|
catalogProxy: draft.catalogProxy.trim(),
|
|
674
675
|
providers: Object.fromEntries(Object.entries(draft.providers).map(([route, provider]) => [route, providerToWire(provider)]))
|
|
675
676
|
};
|
|
@@ -1029,6 +1030,7 @@ ${extra}
|
|
|
1029
1030
|
className: "lpc-grid",
|
|
1030
1031
|
children: fields.map((field) => {
|
|
1031
1032
|
const spec = compatFieldSpec(effective, field);
|
|
1033
|
+
if (spec === void 0) return null;
|
|
1032
1034
|
if (spec === "boolean") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
1033
1035
|
id: `${props.idPrefix}-${field}`,
|
|
1034
1036
|
label: field,
|
package/lib/index.d.ts
CHANGED
|
@@ -80,7 +80,12 @@ interface LlmPiConfig {
|
|
|
80
80
|
catalogProxy: string;
|
|
81
81
|
providers: Record<string, ProviderProfileConfig>;
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* schema 的宽松输入面:cordis 行级 config 与 settings 用户层都可能只给部分键
|
|
85
|
+
* (5 个根字段均有 schema 默认值),解析输出仍是完整的 {@link LlmPiConfig}。
|
|
86
|
+
*/
|
|
87
|
+
type LlmPiConfigInput = Partial<LlmPiConfig>;
|
|
88
|
+
declare const Config: z<LlmPiConfigInput, LlmPiConfig>;
|
|
84
89
|
//#endregion
|
|
85
90
|
//#region src/index.d.ts
|
|
86
91
|
declare const name = "dsh-plus-llm-pi";
|
package/lib/index.js
CHANGED
|
@@ -66,7 +66,14 @@ const DEEPSEEK_REASONING_EFFORTS = [
|
|
|
66
66
|
const DEEPSEEK_IMAGE_DETAILS = ["auto", "low"];
|
|
67
67
|
/** dsh-timeout 的定时器上限(与官方 MAX_TIMER_DELAY_MS 对齐)。 */
|
|
68
68
|
const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
69
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* 键为可选档位,值为线协议拼写;仅 off 可留空(支持但不发送参数)。
|
|
71
|
+
*
|
|
72
|
+
* 类型收窄说明:schemastery 的 z.dict 把输出推断为"全部键必填"(cosmokit 的
|
|
73
|
+
* Dict 是不带可选修饰的映射类型),而运行期语义是"键可选"——profiles.ts 的
|
|
74
|
+
* validateReasoningEfforts 按缺键 = 未声明处理,schema 也只校验给出的键。
|
|
75
|
+
* 故此处只把该节点的类型标注回真实契约,schema 校验行为不变。
|
|
76
|
+
*/
|
|
70
77
|
const reasoningEfforts = z.dict(z.union([z.string(), z.const(null)]), z.union(THINKING_LEVELS));
|
|
71
78
|
const thinkingBudgets = z.object({
|
|
72
79
|
minimal: z.number(),
|
|
@@ -151,7 +158,10 @@ const Config = z.object({
|
|
|
151
158
|
function builtinProviderBaseUrl(kit, provider) {
|
|
152
159
|
return kit.builtinProviders().find((p) => p.id === provider)?.baseUrl;
|
|
153
160
|
}
|
|
154
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* 内置目录是否存在该 provider;同时把运行期配置里的 provider 字符串
|
|
163
|
+
* 收窄为目录键字面量(调用方随后可直调 getBuiltinModels)。
|
|
164
|
+
*/
|
|
155
165
|
function hasBuiltinProvider(kit, provider) {
|
|
156
166
|
return kit.getBuiltinProviders().includes(provider);
|
|
157
167
|
}
|
|
@@ -699,7 +709,7 @@ function deepseekCatalogAnswer(kit, route) {
|
|
|
699
709
|
if (models.length === 0) throw new kit.LlmError("该 route 未配置 models 且未 extends deepseek;请手工录入模型", "DISCOVERY_FAILED");
|
|
700
710
|
return models;
|
|
701
711
|
}
|
|
702
|
-
/** 内置目录直答(route 配了 provider 级 extends
|
|
712
|
+
/** 内置目录直答(route 配了 provider 级 extends 时;入参已由 hasBuiltinProvider 收窄)。 */
|
|
703
713
|
function catalogAnswer(kit, source) {
|
|
704
714
|
return builtinModelIds(kit, source).map((id) => {
|
|
705
715
|
const model = kit.getBuiltinModels(source).find((m) => m.id === id);
|
|
@@ -719,7 +729,7 @@ function catalogAnswer(kit, source) {
|
|
|
719
729
|
async function discoverModels(request, deps) {
|
|
720
730
|
const { kit } = deps;
|
|
721
731
|
const route = request.provider === void 0 ? void 0 : deps.configProviders()[request.provider];
|
|
722
|
-
if ((route
|
|
732
|
+
if (route !== void 0 && (route.adapter ?? "pi") === "deepseek") return deepseekCatalogAnswer(kit, route);
|
|
723
733
|
if (route?.extends !== void 0 && hasBuiltinProvider(kit, route.extends)) return catalogAnswer(kit, route.extends);
|
|
724
734
|
const baseURL = request.baseURL ?? route?.baseURL;
|
|
725
735
|
if (baseURL === void 0 || baseURL.length === 0) throw new kit.LlmError(`route ${JSON.stringify(request.provider ?? "")} 未配 baseURL 且 extends 源无内置目录;无法探测模型清单`, "DISCOVERY_FAILED");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/llm-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "dsh-plus service+ui plugin: 基于 PiAiAdapter 的自定义 LLM 路由(全量 compat、模型继承、models.dev 目录兜底)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,43 +31,43 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"https-proxy-agent": "^9.1.0",
|
|
35
|
+
"@dsh-plus/shared": "0.1.15"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
39
|
-
"@deepseek-ai/dsh-anonymous-user-id": "^0.1.5-
|
|
40
|
-
"@deepseek-ai/dsh-credentials": "^0.1.5-
|
|
41
|
-
"@deepseek-ai/dsh-home-paths": "^0.1.5-
|
|
42
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.5-
|
|
43
|
-
"@deepseek-ai/dsh-launch-environment": "^0.1.5-
|
|
44
|
-
"@deepseek-ai/dsh-llm": "^0.1.5-
|
|
45
|
-
"@deepseek-ai/dsh-llm-deepseek": "^0.1.5-
|
|
46
|
-
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.5-
|
|
47
|
-
"@deepseek-ai/dsh-settings": "^0.1.5-
|
|
48
|
-
"@deepseek-ai/dsh-util-values": "^0.1.5-
|
|
39
|
+
"@deepseek-ai/dsh-anonymous-user-id": "^0.1.5-rc.1",
|
|
40
|
+
"@deepseek-ai/dsh-credentials": "^0.1.5-rc.1",
|
|
41
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.5-rc.1",
|
|
42
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.1",
|
|
43
|
+
"@deepseek-ai/dsh-launch-environment": "^0.1.5-rc.1",
|
|
44
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
|
|
45
|
+
"@deepseek-ai/dsh-llm-deepseek": "^0.1.5-rc.1",
|
|
46
|
+
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.5-rc.1",
|
|
47
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
|
|
48
|
+
"@deepseek-ai/dsh-util-values": "^0.1.5-rc.1",
|
|
49
49
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
50
|
-
"@earendil-works/pi-ai": "^0.
|
|
50
|
+
"@earendil-works/pi-ai": "^0.85.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@deepseek-ai/cordis": "4.0.2",
|
|
54
|
-
"@deepseek-ai/dsh-anonymous-user-id": "0.1.5-
|
|
55
|
-
"@deepseek-ai/dsh-atomic-write": "0.1.5-
|
|
56
|
-
"@deepseek-ai/dsh-attachment": "0.1.5-
|
|
57
|
-
"@deepseek-ai/dsh-credentials": "0.1.5-
|
|
58
|
-
"@deepseek-ai/dsh-home-paths": "0.1.5-
|
|
59
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.5-
|
|
60
|
-
"@deepseek-ai/dsh-launch-environment": "0.1.5-
|
|
61
|
-
"@deepseek-ai/dsh-llm": "0.1.5-
|
|
62
|
-
"@deepseek-ai/dsh-llm-deepseek": "0.1.5-
|
|
63
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.1.5-
|
|
64
|
-
"@deepseek-ai/dsh-settings": "0.1.5-
|
|
65
|
-
"@deepseek-ai/dsh-timeout": "0.1.5-
|
|
66
|
-
"@deepseek-ai/dsh-typert-protocol": "0.1.5-
|
|
67
|
-
"@deepseek-ai/dsh-util-crypto": "0.1.5-
|
|
68
|
-
"@deepseek-ai/dsh-util-values": "0.1.5-
|
|
54
|
+
"@deepseek-ai/dsh-anonymous-user-id": "0.1.5-rc.1",
|
|
55
|
+
"@deepseek-ai/dsh-atomic-write": "0.1.5-rc.1",
|
|
56
|
+
"@deepseek-ai/dsh-attachment": "0.1.5-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-credentials": "0.1.5-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.1",
|
|
60
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.5-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-llm-deepseek": "0.1.5-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.1.5-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-timeout": "0.1.5-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-util-crypto": "0.1.5-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-util-values": "0.1.5-rc.1",
|
|
69
69
|
"@deepseek-ai/schemastery": "3.18.2",
|
|
70
|
-
"@earendil-works/pi-ai": "0.
|
|
70
|
+
"@earendil-works/pi-ai": "0.85.1",
|
|
71
71
|
"@types/react": "~18.3.31",
|
|
72
72
|
"react": "^18.2.0"
|
|
73
73
|
},
|
package/src/catalog/builtin.ts
CHANGED
|
@@ -28,14 +28,21 @@ export interface ModelBase {
|
|
|
28
28
|
headers?: Record<string, string>
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** 内置目录的 provider id 字面量联合(pi-ai 生成目录的键集)。 */
|
|
32
|
+
export type BuiltinProviderId = ReturnType<DshKit['getBuiltinProviders']>[number]
|
|
33
|
+
|
|
31
34
|
/** 内置 provider 的端点(provider 级 extends 的 baseURL 缺省值)。 */
|
|
32
35
|
export function builtinProviderBaseUrl(kit: DshKit, provider: string): string | undefined {
|
|
33
36
|
return kit.builtinProviders().find((p) => p.id === provider)?.baseUrl
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
/**
|
|
40
|
+
* 内置目录是否存在该 provider;同时把运行期配置里的 provider 字符串
|
|
41
|
+
* 收窄为目录键字面量(调用方随后可直调 getBuiltinModels)。
|
|
42
|
+
*/
|
|
43
|
+
export function hasBuiltinProvider(kit: DshKit, provider: string): provider is BuiltinProviderId {
|
|
44
|
+
const ids: readonly string[] = kit.getBuiltinProviders()
|
|
45
|
+
return ids.includes(provider)
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/** 内置 provider 的全部模型 id(UI extends 选择器用)。 */
|
package/src/catalog/official.ts
CHANGED
|
@@ -15,7 +15,8 @@ export interface OfficialModelBase {
|
|
|
15
15
|
contextWindow?: number
|
|
16
16
|
maxTokens?: number
|
|
17
17
|
input?: ('text' | 'image')[]
|
|
18
|
-
|
|
18
|
+
/** 官方目录取值为像素数或 'low' 预设(DeepSeekCatalogModel 同域;透传给官方 schema 校验)。 */
|
|
19
|
+
imagePixelBudget?: number | 'low'
|
|
19
20
|
imageMaxBytes?: number
|
|
20
21
|
}
|
|
21
22
|
|
package/src/client/draft.ts
CHANGED
|
@@ -206,8 +206,8 @@ const KNOWN_PROVIDER_KEYS = new Set([
|
|
|
206
206
|
'models',
|
|
207
207
|
])
|
|
208
208
|
|
|
209
|
-
/** 摘出 wire 对象里卡片未认识的字段(undefined
|
|
210
|
-
function extraOf(wire:
|
|
209
|
+
/** 摘出 wire 对象里卡片未认识的字段(undefined 值不保留;入参只要求是普通对象)。 */
|
|
210
|
+
function extraOf(wire: object, known: Set<string>): Record<string, unknown> {
|
|
211
211
|
const out: Record<string, unknown> = {}
|
|
212
212
|
for (const [key, value] of Object.entries(wire)) {
|
|
213
213
|
if (!known.has(key) && value !== undefined) out[key] = value
|
|
@@ -225,7 +225,7 @@ function modelDraftFromWire(model: WireModel): ModelDraft {
|
|
|
225
225
|
input: inputFromWire(model.input),
|
|
226
226
|
reasoningEfforts: reasoningFromWire(model.reasoningEfforts),
|
|
227
227
|
compat: { ...(model.compat ?? {}) },
|
|
228
|
-
extra: extraOf(model
|
|
228
|
+
extra: extraOf(model, KNOWN_MODEL_KEYS),
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
|
|
@@ -268,7 +268,7 @@ function providerDraftFromWire(provider: WireProvider): ProviderDraft {
|
|
|
268
268
|
maxRequestImageBytes: numToText(provider.maxRequestImageBytes),
|
|
269
269
|
retryPolicy: provider.retryPolicy,
|
|
270
270
|
models: (provider.models ?? []).map(modelDraftFromWire),
|
|
271
|
-
extra: extraOf(provider
|
|
271
|
+
extra: extraOf(provider, KNOWN_PROVIDER_KEYS),
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -357,10 +357,14 @@ export function draftFromValue(value: ConfigValue): Draft {
|
|
|
357
357
|
|
|
358
358
|
/** 提交补丁:完整配置对象,providers 全量替换;空值一律剔除。 */
|
|
359
359
|
export function toPatch(draft: Draft): ConfigPatch {
|
|
360
|
+
const refreshHours = toNum(draft.catalogRefreshHours)
|
|
360
361
|
return {
|
|
361
362
|
enabled: draft.enabled,
|
|
362
363
|
catalogUrl: draft.catalogUrl.trim(),
|
|
363
|
-
|
|
364
|
+
// 保存动作在文本非法时不可用(card.tsx 的 invalid 门控),故写入路径必为合法数字;
|
|
365
|
+
// dirty 比较路径可能拿到非法文本的 undefined(JSON.stringify 时同键被丢弃),
|
|
366
|
+
// 此处仅把"保存前已校验"的运行期事实带到类型层,取值不变。
|
|
367
|
+
catalogRefreshHours: refreshHours as unknown as number,
|
|
364
368
|
catalogProxy: draft.catalogProxy.trim(),
|
|
365
369
|
providers: Object.fromEntries(
|
|
366
370
|
Object.entries(draft.providers).map(([route, provider]) => [route, providerToWire(provider)]),
|
package/src/client/fields.tsx
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { ChevronDownIcon } from '@dsh-plus/shared/client'
|
|
9
|
-
import { type ReactElement, useEffect, useState } from 'react'
|
|
9
|
+
import { type ReactElement, type ReactNode, useEffect, useState } from 'react'
|
|
10
10
|
|
|
11
11
|
import type { HeaderPair } from './draft.ts'
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ export interface CollapseSectionProps {
|
|
|
20
20
|
/** 默认展开态;复杂项传 false(默认折叠)。 */
|
|
21
21
|
defaultOpen: boolean
|
|
22
22
|
disabled?: boolean
|
|
23
|
-
children:
|
|
23
|
+
children: ReactNode
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function CollapseSection(props: CollapseSectionProps): ReactElement {
|
|
@@ -56,6 +56,8 @@ export function CompatEditor(props: CompatEditorProps): ReactElement {
|
|
|
56
56
|
<div className="lpc-grid">
|
|
57
57
|
{fields.map((field) => {
|
|
58
58
|
const spec = compatFieldSpec(effective, field)
|
|
59
|
+
// 键取自同一张表(compatFieldsOf),spec 不可能为 undefined;此处仅收窄类型
|
|
60
|
+
if (spec === undefined) return null
|
|
59
61
|
if (spec === 'boolean') {
|
|
60
62
|
return (
|
|
61
63
|
<SelectField
|
package/src/config-api.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
9
9
|
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis'
|
|
11
|
+
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
11
12
|
|
|
12
13
|
import { builtinModelIds } from './catalog/builtin.ts'
|
|
13
14
|
import type { LlmPiRuntime } from './service.ts'
|
package/src/config.ts
CHANGED
|
@@ -52,8 +52,18 @@ export const DEFAULT_REQUEST_IMAGE_MAX_BYTES = 1024 * 1024
|
|
|
52
52
|
/** dsh-timeout 的定时器上限(与官方 MAX_TIMER_DELAY_MS 对齐)。 */
|
|
53
53
|
export const MAX_TIMER_DELAY_MS = 2 ** 31 - 1
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
55
|
+
/**
|
|
56
|
+
* 键为可选档位,值为线协议拼写;仅 off 可留空(支持但不发送参数)。
|
|
57
|
+
*
|
|
58
|
+
* 类型收窄说明:schemastery 的 z.dict 把输出推断为"全部键必填"(cosmokit 的
|
|
59
|
+
* Dict 是不带可选修饰的映射类型),而运行期语义是"键可选"——profiles.ts 的
|
|
60
|
+
* validateReasoningEfforts 按缺键 = 未声明处理,schema 也只校验给出的键。
|
|
61
|
+
* 故此处只把该节点的类型标注回真实契约,schema 校验行为不变。
|
|
62
|
+
*/
|
|
63
|
+
const reasoningEfforts = z.dict(
|
|
64
|
+
z.union([z.string(), z.const(null)]),
|
|
65
|
+
z.union(THINKING_LEVELS),
|
|
66
|
+
) as unknown as z<ReasoningEfforts>
|
|
57
67
|
|
|
58
68
|
export type ThinkingLevel = (typeof THINKING_LEVELS)[number]
|
|
59
69
|
export type Modality = (typeof MODALITIES)[number]
|
|
@@ -127,6 +137,12 @@ export interface LlmPiConfig {
|
|
|
127
137
|
providers: Record<string, ProviderProfileConfig>
|
|
128
138
|
}
|
|
129
139
|
|
|
140
|
+
/**
|
|
141
|
+
* schema 的宽松输入面:cordis 行级 config 与 settings 用户层都可能只给部分键
|
|
142
|
+
* (5 个根字段均有 schema 默认值),解析输出仍是完整的 {@link LlmPiConfig}。
|
|
143
|
+
*/
|
|
144
|
+
export type LlmPiConfigInput = Partial<LlmPiConfig>
|
|
145
|
+
|
|
130
146
|
const thinkingBudgets = z.object({
|
|
131
147
|
minimal: z.number(),
|
|
132
148
|
low: z.number(),
|
|
@@ -302,7 +318,7 @@ const providerProfile = z.object({
|
|
|
302
318
|
.description('本 route 的模型目录;缺省且 provider 有 extends 时继承该源全部模型'),
|
|
303
319
|
})
|
|
304
320
|
|
|
305
|
-
export const Config: z<LlmPiConfig> = z.object({
|
|
321
|
+
export const Config: z<LlmPiConfigInput, LlmPiConfig> = z.object({
|
|
306
322
|
enabled: z.boolean().description('总开关(关闭则不注册任何 route)').default(true),
|
|
307
323
|
catalogUrl: z
|
|
308
324
|
.string()
|
package/src/discovery.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* GET {baseURL}/models、4MB 上限、署名头)与官方一致。结果不落盘。
|
|
7
7
|
* @module llm-pi/discovery
|
|
8
8
|
*/
|
|
9
|
-
import { builtinModelIds, hasBuiltinProvider } from './catalog/builtin.ts'
|
|
9
|
+
import { type BuiltinProviderId, builtinModelIds, hasBuiltinProvider } from './catalog/builtin.ts'
|
|
10
10
|
import { officialModelIds } from './catalog/official.ts'
|
|
11
11
|
import type { ProviderProfileConfig } from './config.ts'
|
|
12
12
|
import type { DshKit } from './resolve-dsh.ts'
|
|
@@ -129,8 +129,8 @@ function deepseekCatalogAnswer(kit: DshKit, route: ProviderProfileConfig): Disco
|
|
|
129
129
|
return models
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
/** 内置目录直答(route 配了 provider 级 extends
|
|
133
|
-
function catalogAnswer(kit: DshKit, source:
|
|
132
|
+
/** 内置目录直答(route 配了 provider 级 extends 时;入参已由 hasBuiltinProvider 收窄)。 */
|
|
133
|
+
function catalogAnswer(kit: DshKit, source: BuiltinProviderId): DiscoveryEntry[] {
|
|
134
134
|
return builtinModelIds(kit, source).map((id) => {
|
|
135
135
|
const models = kit.getBuiltinModels(source)
|
|
136
136
|
const model = models.find((m) => m.id === id)
|
|
@@ -155,7 +155,9 @@ export async function discoverModels(
|
|
|
155
155
|
const { kit } = deps
|
|
156
156
|
const route: ProviderProfileConfig | undefined =
|
|
157
157
|
request.provider === undefined ? undefined : deps.configProviders()[request.provider]
|
|
158
|
-
|
|
158
|
+
// route 为空时 (route?.adapter ?? 'pi') 恒为 'pi',故显式判空不改变分支结果,
|
|
159
|
+
// 只用于把 route 收窄为已定义(供 deepseekCatalogAnswer 使用)。
|
|
160
|
+
if (route !== undefined && (route.adapter ?? 'pi') === 'deepseek') {
|
|
159
161
|
return deepseekCatalogAnswer(kit, route)
|
|
160
162
|
}
|
|
161
163
|
if (route?.extends !== undefined && hasBuiltinProvider(kit, route.extends)) {
|
package/src/profiles.ts
CHANGED
|
@@ -628,7 +628,10 @@ export function resolveProfilesFallback(
|
|
|
628
628
|
name: displayName,
|
|
629
629
|
...(source.baseURL === undefined ? {} : { baseUrl: source.baseURL }),
|
|
630
630
|
auth: { apiKey: harnessApiKeyAuth(displayName) },
|
|
631
|
-
|
|
631
|
+
// 物化条目的 compat 是开放字典(写时经 validateCompat 按 api 门控校验),
|
|
632
|
+
// 与 pi-ai Model 的逐协议 compat 类型不同构;运行期仅由 pi-ai 按字段读取,
|
|
633
|
+
// 形状与官方 resolveProfiles 产物一致,故此处按目标契约收窄。
|
|
634
|
+
models: models as unknown as Parameters<DshKit['createProvider']>[0]['models'],
|
|
632
635
|
api: factory() as never,
|
|
633
636
|
}),
|
|
634
637
|
} as ResolvedPiAiProviderProfile)
|
package/src/resolve-dsh.ts
CHANGED
|
@@ -131,7 +131,12 @@ function assertKitShape(kit: DshKit, origin: string): void {
|
|
|
131
131
|
'prepareCall',
|
|
132
132
|
'providerRetryPolicy',
|
|
133
133
|
] as const) {
|
|
134
|
-
|
|
134
|
+
// 原型方法存在性自检:类实例的原型在运行期是普通对象,按方法名索引读取安全
|
|
135
|
+
// (类类型本身无索引签名,故经 unknown 转字典视图;形状由本函数逐项断言)。
|
|
136
|
+
if (
|
|
137
|
+
typeof (kit.PiAiAdapter.prototype as unknown as Record<string, unknown>)[method] !==
|
|
138
|
+
'function'
|
|
139
|
+
) {
|
|
135
140
|
problems.push(`PiAiAdapter.prototype.${method} 缺失`)
|
|
136
141
|
}
|
|
137
142
|
}
|
|
@@ -164,7 +169,9 @@ function checkDeepseekShape(kit: DeepSeekKit): string[] {
|
|
|
164
169
|
const problems: string[] = []
|
|
165
170
|
if (typeof kit.DeepSeekAdapter !== 'function') problems.push('DeepSeekAdapter 不是类')
|
|
166
171
|
else if (
|
|
167
|
-
|
|
172
|
+
// 同 assertKitShape:原型方法存在性自检,经 unknown 转字典视图读取。
|
|
173
|
+
typeof (kit.DeepSeekAdapter.prototype as unknown as Record<string, unknown>)['stream'] !==
|
|
174
|
+
'function'
|
|
168
175
|
) {
|
|
169
176
|
problems.push('DeepSeekAdapter.prototype.stream 缺失')
|
|
170
177
|
}
|
package/src/service.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import type { Context } from '@deepseek-ai/cordis'
|
|
13
13
|
import type { CredentialRef } from '@deepseek-ai/dsh-credentials'
|
|
14
14
|
import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'
|
|
15
|
+
import type { DirectoryRegistrationHandle } from '@deepseek-ai/dsh-llm'
|
|
15
16
|
import type { ResolvedPiAiProviderProfile } from '@deepseek-ai/dsh-llm-pi-ai'
|
|
16
17
|
import type {} from '@deepseek-ai/dsh-settings'
|
|
17
18
|
import { deepEqualJson } from '@deepseek-ai/dsh-util-values'
|
|
@@ -237,7 +238,7 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
237
238
|
registeredFacts = facts
|
|
238
239
|
}
|
|
239
240
|
|
|
240
|
-
let directory:
|
|
241
|
+
let directory: DirectoryRegistrationHandle | undefined
|
|
241
242
|
let directoryFacts: unknown
|
|
242
243
|
/** 草稿路由(无模型占位)从原始配置直读——它们不进 adapter profiles。 */
|
|
243
244
|
const draftRoutes = (): { route: string; displayName: string }[] => {
|
|
@@ -263,8 +264,7 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
263
264
|
}
|
|
264
265
|
commitDirectory(
|
|
265
266
|
(batch: DirectoryEntry[]) => {
|
|
266
|
-
if (directory === undefined)
|
|
267
|
-
directory = ctx.llm.registerConfigurableProviders(batch as never)
|
|
267
|
+
if (directory === undefined) directory = ctx.llm.registerConfigurableProviders(batch)
|
|
268
268
|
else directory.replace(batch)
|
|
269
269
|
},
|
|
270
270
|
entries,
|