@dsh-plus/llm-pi 0.1.42 → 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 +1489 -636
- 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 +180 -120
- package/src/client/client.ts +2 -2
- 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/{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/resolve-dsh.ts
CHANGED
|
@@ -7,10 +7,17 @@
|
|
|
7
7
|
* 桌面端 Electron 下 argv[1] 探测走不通)→ realpath(argv[1]) 向上找到同时
|
|
8
8
|
* 含 node_modules/@deepseek-ai/dsh-llm-pi-ai 与 node_modules/@earendil-works/pi-ai
|
|
9
9
|
* 的目录,按文件路径动态 import——与 dsh 官方插件共享同一模块实例;
|
|
10
|
-
* 2. vendored:回退到本插件
|
|
10
|
+
* 2. vendored:回退到本插件 devDependencies 里的副本(裸 import)。
|
|
11
11
|
*
|
|
12
|
-
* 两条路径产物都过形状自检(assertKitShape
|
|
13
|
-
*
|
|
12
|
+
* 两条路径产物都过形状自检(assertKitShape):上游重构导致形状漂移时抛错,
|
|
13
|
+
* 由调用方决定回退或放弃注册——绝不让坏套件进入消息链路。
|
|
14
|
+
*
|
|
15
|
+
* **自动跟随(版本不参与分支)**:插件不认识"某个 pi-ai 版本",只认识形状与
|
|
16
|
+
* 现场推导出的适配面。协议集合取自已装官方包的 `supportedProtocols()`,每个协议
|
|
17
|
+
* 的惰性工厂按 `pi-ai/dist/api/<api>.lazy.js`(api id 即模块名,pi-ai 包导出约定)
|
|
18
|
+
* 动态加载;缺失的协议逐项跳过并记诊断,其余协议照常。compat 门控/取值约束与
|
|
19
|
+
* 模型条目字段集同样现场推导(official-surface.ts)。生效版本只用于状态行显示,
|
|
20
|
+
* 超出已验证区间也只是提示(kit-meta.ts),永不阻断。
|
|
14
21
|
*
|
|
15
22
|
* 0.1.2-alpha 线的新面(0.1.2-alpha.2 复核包根仍未导出、仅 src 子路径有):
|
|
16
23
|
* - resolveProfiles(config.ts):官方解析链,dev 布局树可经 src 子路径复用;
|
|
@@ -55,8 +62,16 @@ import {
|
|
|
55
62
|
credentialStoreFrom as inlineCredentialStoreFrom,
|
|
56
63
|
} from './auth-inline.ts'
|
|
57
64
|
import { installCompatTable } from './compat.ts'
|
|
58
|
-
import {
|
|
59
|
-
import
|
|
65
|
+
import { collectTreeVersions, collectVendoredVersions, type KitVersions } from './kit-meta.ts'
|
|
66
|
+
import {
|
|
67
|
+
deriveGates,
|
|
68
|
+
deriveModelEntryFields,
|
|
69
|
+
deriveProtocols,
|
|
70
|
+
deriveSpecs,
|
|
71
|
+
FALLBACK_MODEL_ENTRY_FIELDS,
|
|
72
|
+
FALLBACK_TABLE,
|
|
73
|
+
fallbackProtocols,
|
|
74
|
+
} from './official-surface.ts'
|
|
60
75
|
import { type ResolverDeps, resolveProfilesFallback } from './profiles.ts'
|
|
61
76
|
|
|
62
77
|
/**
|
|
@@ -88,6 +103,19 @@ export interface AuthHelpers {
|
|
|
88
103
|
export interface DshKit {
|
|
89
104
|
/** 解析来源:dsh 安装树 / 插件 vendored 兜底副本。 */
|
|
90
105
|
source: 'dsh-tree' | 'vendored'
|
|
106
|
+
/** 套件所在根(dsh 安装树根 / vendored 包所在 node_modules 的父目录),诊断用。 */
|
|
107
|
+
root?: string
|
|
108
|
+
/** 实际生效版本(状态行显示;不参与任何分支判断)。 */
|
|
109
|
+
versions: KitVersions
|
|
110
|
+
/** 当前可服务的线协议(顺序即官方表顺序;无工厂的协议已被剔除)。 */
|
|
111
|
+
protocols: string[]
|
|
112
|
+
/** 协议集合来源:official = 官方 supportedProtocols();fallback = 内置三元组。 */
|
|
113
|
+
protocolSource: 'official' | 'fallback'
|
|
114
|
+
protocolFactories: Record<string, () => unknown>
|
|
115
|
+
/** 官方 Config schema 接受的模型条目键集(继承透传白名单,现场推导)。 */
|
|
116
|
+
officialModelFields: string[]
|
|
117
|
+
/** 内置目录数据生成时间(毫秒);官方未导出该信息时省略。 */
|
|
118
|
+
catalogGeneratedAt?: number
|
|
91
119
|
PiAiAdapter: typeof PiAiAdapterType
|
|
92
120
|
LlmError: typeof DshLlm.LlmError
|
|
93
121
|
resolveRetryPolicy: typeof DshLlm.resolveRetryPolicy
|
|
@@ -99,8 +127,6 @@ export interface DshKit {
|
|
|
99
127
|
builtinProviders: typeof vendoredCatalog.builtinProviders
|
|
100
128
|
getBuiltinProviders: typeof vendoredCatalog.getBuiltinProviders
|
|
101
129
|
getBuiltinModels: typeof vendoredCatalog.getBuiltinModels
|
|
102
|
-
/** 三协议的 pi-ai api 实现工厂(与官方 PROTOCOLS 表同来源)。 */
|
|
103
|
-
protocolFactories: Record<ProtocolId, () => unknown>
|
|
104
130
|
/**
|
|
105
131
|
* profile 解析链:dsh 树 dev 布局(src/config.ts 存在)时为官方
|
|
106
132
|
* resolveProfiles;npm 形态(无 src)与 vendored 兜底时为插件等价实现。
|
|
@@ -149,9 +175,13 @@ function assertKitShape(kit: DshKit, origin: string): void {
|
|
|
149
175
|
if (typeof kit.createProvider !== 'function') problems.push('pi-ai createProvider 缺失')
|
|
150
176
|
if (typeof kit.getBuiltinModels !== 'function') problems.push('pi-ai getBuiltinModels 缺失')
|
|
151
177
|
if (typeof kit.builtinProviders !== 'function') problems.push('pi-ai builtinProviders 缺失')
|
|
152
|
-
|
|
153
|
-
|
|
178
|
+
if (kit.protocols.length === 0) problems.push('没有可服务的线协议(协议工厂全部加载失败)')
|
|
179
|
+
for (const api of kit.protocols) {
|
|
180
|
+
if (typeof kit.protocolFactories[api] !== 'function') {
|
|
181
|
+
problems.push(`协议 ${api} 的工厂缺失`)
|
|
182
|
+
}
|
|
154
183
|
}
|
|
184
|
+
if (kit.officialModelFields.length === 0) problems.push('官方模型条目字段集为空')
|
|
155
185
|
if (typeof kit.LlmError !== 'function') problems.push('dsh-llm LlmError 缺失')
|
|
156
186
|
if (typeof kit.resolveRetryPolicy !== 'function') problems.push('dsh-llm resolveRetryPolicy 缺失')
|
|
157
187
|
if (typeof kit.resolveProfiles !== 'function') problems.push('resolveProfiles 缺失')
|
|
@@ -206,31 +236,26 @@ function findDshTreeRoot(startDir: string): string | undefined {
|
|
|
206
236
|
return undefined
|
|
207
237
|
}
|
|
208
238
|
|
|
239
|
+
/** dsh 树内与官方插件同源加载的核心模块(协议工厂按推导结果另加载)。 */
|
|
209
240
|
interface TreeModules {
|
|
210
241
|
piAiAdapter: Record<string, unknown>
|
|
211
242
|
llm: Record<string, unknown>
|
|
212
243
|
piAi: Record<string, unknown>
|
|
213
244
|
catalog: Record<string, unknown>
|
|
214
|
-
completions: Record<string, unknown>
|
|
215
|
-
responses: Record<string, unknown>
|
|
216
|
-
anthropic: Record<string, unknown>
|
|
217
245
|
}
|
|
218
246
|
|
|
219
|
-
/** 从 dsh 安装树按文件路径动态 import
|
|
247
|
+
/** 从 dsh 安装树按文件路径动态 import 核心套件模块(与官方插件同实例)。 */
|
|
220
248
|
async function importTreeModules(root: string): Promise<TreeModules> {
|
|
221
249
|
const nm = join(root, 'node_modules')
|
|
222
250
|
const load = (absPath: string): Promise<Record<string, unknown>> =>
|
|
223
251
|
import(pathToFileURL(absPath).href) as Promise<Record<string, unknown>>
|
|
224
|
-
const [piAiAdapter, llm, piAi, catalog
|
|
252
|
+
const [piAiAdapter, llm, piAi, catalog] = await Promise.all([
|
|
225
253
|
load(join(nm, '@deepseek-ai', 'dsh-llm-pi-ai', 'lib', 'index.js')),
|
|
226
254
|
load(join(nm, '@deepseek-ai', 'dsh-llm', 'lib', 'index.js')),
|
|
227
255
|
load(join(nm, '@earendil-works', 'pi-ai', 'dist', 'index.js')),
|
|
228
256
|
load(join(nm, '@earendil-works', 'pi-ai', 'dist', 'providers', 'all.js')),
|
|
229
|
-
load(join(nm, '@earendil-works', 'pi-ai', 'dist', 'api', 'openai-completions.lazy.js')),
|
|
230
|
-
load(join(nm, '@earendil-works', 'pi-ai', 'dist', 'api', 'openai-responses.lazy.js')),
|
|
231
|
-
load(join(nm, '@earendil-works', 'pi-ai', 'dist', 'api', 'anthropic-messages.lazy.js')),
|
|
232
257
|
])
|
|
233
|
-
return { piAiAdapter, llm, piAi, catalog
|
|
258
|
+
return { piAiAdapter, llm, piAi, catalog }
|
|
234
259
|
}
|
|
235
260
|
|
|
236
261
|
/**
|
|
@@ -282,72 +307,227 @@ async function importTreeDeepseek(root: string): Promise<{ kit?: DeepSeekKit; pr
|
|
|
282
307
|
}
|
|
283
308
|
}
|
|
284
309
|
|
|
285
|
-
|
|
310
|
+
/**
|
|
311
|
+
* 取惰性协议模块里的工厂:约定每个 `api/<api>.lazy.js` 只导出一个 `*Api`
|
|
312
|
+
* 工厂函数。名字不符时退化为"唯一函数导出",多函数且无 `*Api` 命名则判失败
|
|
313
|
+
* (宁可跳过该协议,也不猜错工厂)。
|
|
314
|
+
*/
|
|
315
|
+
function protocolFactoryOf(module: Record<string, unknown>): (() => unknown) | undefined {
|
|
316
|
+
const functions = Object.entries(module).filter(
|
|
317
|
+
(entry): entry is [string, () => unknown] => typeof entry[1] === 'function',
|
|
318
|
+
)
|
|
319
|
+
const named = functions.filter(([name]) => /Api$/.test(name))
|
|
320
|
+
if (named.length === 1) return named[0]?.[1]
|
|
321
|
+
if (functions.length === 1) return functions[0]?.[1]
|
|
322
|
+
return undefined
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** pi-ai 的惰性协议实现目录(dsh 树布局)。 */
|
|
326
|
+
function piAiApiDir(root: string): string {
|
|
327
|
+
return join(root, 'node_modules', '@earendil-works', 'pi-ai', 'dist', 'api')
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** 内置三元组的静态加载(协议推导失败或全部工厂加载失败时的兜底)。 */
|
|
331
|
+
async function loadFallbackTreeFactories(
|
|
332
|
+
root: string,
|
|
333
|
+
diagnostics: string[],
|
|
334
|
+
): Promise<Record<string, () => unknown>> {
|
|
335
|
+
const dir = piAiApiDir(root)
|
|
336
|
+
const out: Record<string, () => unknown> = {}
|
|
337
|
+
for (const api of fallbackProtocols()) {
|
|
338
|
+
const file = join(dir, `${api}.lazy.js`)
|
|
339
|
+
if (!existsSync(file)) continue
|
|
340
|
+
try {
|
|
341
|
+
const factory = protocolFactoryOf((await import(pathToFileURL(file).href)) as never)
|
|
342
|
+
if (factory !== undefined) out[api] = factory
|
|
343
|
+
} catch {
|
|
344
|
+
// 单个兜底协议加载失败:静默留给 assertKitShape 汇总(下方诊断已说明回退)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (Object.keys(out).length < fallbackProtocols().length) {
|
|
348
|
+
diagnostics.push(
|
|
349
|
+
`pi-ai 协议兜底三元组未能全部加载(${Object.keys(out).join('/') || '空'});` +
|
|
350
|
+
'请检查 dsh 安装树里 @earendil-works/pi-ai/dist/api 的形态',
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
return out
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* 按官方声明的协议集合加载 pi-ai 惰性工厂(api id 即模块名)。
|
|
358
|
+
* 逐项失败只跳过该协议并记诊断;一个都加载不到时回退内置三元组。
|
|
359
|
+
*/
|
|
360
|
+
async function loadTreeProtocols(
|
|
361
|
+
root: string,
|
|
362
|
+
derived: string[],
|
|
363
|
+
diagnostics: string[],
|
|
364
|
+
): Promise<{ protocols: string[]; protocolFactories: Record<string, () => unknown> }> {
|
|
365
|
+
const dir = piAiApiDir(root)
|
|
366
|
+
const out: Record<string, () => unknown> = {}
|
|
367
|
+
for (const api of derived) {
|
|
368
|
+
const file = join(dir, `${api}.lazy.js`)
|
|
369
|
+
if (!existsSync(file)) {
|
|
370
|
+
diagnostics.push(
|
|
371
|
+
`pi-ai 未提供协议 "${api}" 的惰性实现(缺 ${file});该协议不可用,其余协议不受影响`,
|
|
372
|
+
)
|
|
373
|
+
continue
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const factory = protocolFactoryOf((await import(pathToFileURL(file).href)) as never)
|
|
377
|
+
if (factory === undefined) {
|
|
378
|
+
diagnostics.push(`协议 "${api}" 的实现模块未导出唯一 *Api 工厂;该协议不可用`)
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
out[api] = factory
|
|
382
|
+
} catch (error) {
|
|
383
|
+
diagnostics.push(
|
|
384
|
+
`协议 "${api}" 的实现加载失败(${error instanceof Error ? error.message : String(error)});该协议不可用`,
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (Object.keys(out).length > 0) {
|
|
389
|
+
return { protocols: derived.filter((api) => out[api] !== undefined), protocolFactories: out }
|
|
390
|
+
}
|
|
391
|
+
diagnostics.push('官方声明的协议一个都加载不到;回退内置三元组')
|
|
392
|
+
const fallback = await loadFallbackTreeFactories(root, diagnostics)
|
|
286
393
|
return {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
394
|
+
protocols: fallbackProtocols().filter((api) => fallback[api] !== undefined),
|
|
395
|
+
protocolFactories: fallback,
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function protocolFactoriesOf(
|
|
400
|
+
protocols: string[],
|
|
401
|
+
factories: Record<string, () => unknown>,
|
|
402
|
+
): DshKit['protocolFactories'] {
|
|
403
|
+
const out: DshKit['protocolFactories'] = {}
|
|
404
|
+
for (const api of protocols) {
|
|
405
|
+
const factory = factories[api]
|
|
406
|
+
if (factory !== undefined) out[api] = factory
|
|
290
407
|
}
|
|
408
|
+
return out
|
|
291
409
|
}
|
|
292
410
|
|
|
293
411
|
/** 插件等价解析链的套件依赖(与 PiAiAdapter 同源,杜绝跨源混用)。 */
|
|
294
412
|
function treeResolverDeps(
|
|
295
413
|
mods: TreeModules,
|
|
296
414
|
protocolFactories: DshKit['protocolFactories'],
|
|
415
|
+
protocols: string[],
|
|
297
416
|
): ResolverDeps {
|
|
298
417
|
return {
|
|
299
418
|
createProvider: mods.piAi['createProvider'] as DshKit['createProvider'],
|
|
300
419
|
protocolFactories,
|
|
420
|
+
protocols,
|
|
301
421
|
resolveRetryPolicy: mods.llm['resolveRetryPolicy'] as DshKit['resolveRetryPolicy'],
|
|
302
422
|
}
|
|
303
423
|
}
|
|
304
424
|
|
|
425
|
+
/** 官方适配面推导结果(协议/模型字段/compat 表/compat 来源)。 */
|
|
426
|
+
interface OfficialSurface {
|
|
427
|
+
protocols: string[]
|
|
428
|
+
protocolSource: 'official' | 'fallback'
|
|
429
|
+
officialModelFields: string[]
|
|
430
|
+
catalogGeneratedAt?: number
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** 读内置目录数据生成时间(官方未导出/形状变化时省略)。 */
|
|
434
|
+
function catalogGeneratedAtOf(catalog: Record<string, unknown>): number | undefined {
|
|
435
|
+
const reader = catalog['getBuiltinModelDataGeneratedAt']
|
|
436
|
+
if (typeof reader !== 'function') return undefined
|
|
437
|
+
try {
|
|
438
|
+
const value = (reader as () => unknown)()
|
|
439
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
|
|
440
|
+
} catch {
|
|
441
|
+
return undefined
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
305
445
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
446
|
+
* 从官方安装副本推导适配面并安装 compat 生效表(bundle 文本给分型、Config schema
|
|
447
|
+
* 给取值约束与模型条目字段集、supportedProtocols() 给协议集合)。
|
|
448
|
+
* 任一环节失败都逐项回退并记诊断——绝不因推导失败弄挂插件启动。
|
|
309
449
|
*/
|
|
310
|
-
function
|
|
311
|
-
bundlePath: string
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
450
|
+
function installOfficialSurface(options: {
|
|
451
|
+
bundlePath: string
|
|
452
|
+
adapterModule: Record<string, unknown>
|
|
453
|
+
catalogModule: Record<string, unknown>
|
|
454
|
+
origin: string
|
|
455
|
+
diagnostics: string[]
|
|
456
|
+
}): OfficialSurface {
|
|
457
|
+
const { bundlePath, adapterModule, origin, diagnostics } = options
|
|
458
|
+
const compatLabel = 'compat 门控表'
|
|
316
459
|
try {
|
|
317
460
|
const bundle = readFileSync(bundlePath, 'utf8')
|
|
318
461
|
const gates = deriveGates(bundle)
|
|
319
462
|
if (gates === undefined) {
|
|
320
463
|
// 官方改打包形态导致 COMPAT_GATES 不可解析:用冻结快照并告警。
|
|
464
|
+
// 快照模式下未知 compat 键**放行**(交给官方自身校验),只拦快照里明确的
|
|
465
|
+
// withhold——表落后时"误拒官方新字段"比"多放一个键"严重得多。
|
|
321
466
|
diagnostics.push(
|
|
322
|
-
`${
|
|
323
|
-
|
|
467
|
+
`${compatLabel}未能从官方 bundle 解析 COMPAT_GATES(${bundlePath});` +
|
|
468
|
+
`使用内置快照(未知字段放行、withhold 仍拒绝;请检查 dsh-llm-pi-ai 打包形态)`,
|
|
324
469
|
)
|
|
325
470
|
installCompatTable(FALLBACK_TABLE)
|
|
326
|
-
|
|
471
|
+
} else {
|
|
472
|
+
// 取值约束:推导值优先,内置快照补缺(官方 schema 未覆盖的字段保持可写)。
|
|
473
|
+
const specs = { ...FALLBACK_TABLE.specs, ...deriveSpecs(adapterModule) }
|
|
474
|
+
installCompatTable({ gates, specs, source: 'official' })
|
|
327
475
|
}
|
|
328
|
-
// 取值约束:推导值优先,内置快照补缺(官方 schema 未覆盖的字段保持可写)。
|
|
329
|
-
const specs = { ...FALLBACK_TABLE.specs, ...deriveSpecs(module) }
|
|
330
|
-
installCompatTable({ gates, specs, source: 'official' })
|
|
331
476
|
} catch (error) {
|
|
332
477
|
const reason = error instanceof Error ? error.message : String(error)
|
|
333
|
-
diagnostics.push(`${
|
|
478
|
+
diagnostics.push(`${compatLabel}推导失败(${reason});使用内置快照(未知字段放行)`)
|
|
334
479
|
installCompatTable(FALLBACK_TABLE)
|
|
335
480
|
}
|
|
481
|
+
|
|
482
|
+
const derivedProtocols = deriveProtocols(adapterModule)
|
|
483
|
+
if (derivedProtocols === undefined) {
|
|
484
|
+
diagnostics.push(
|
|
485
|
+
`${origin}:官方 supportedProtocols() 不可用;协议集合回退内置三元组 ${fallbackProtocols().join('/')}`,
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
const fields = deriveModelEntryFields(adapterModule)
|
|
489
|
+
if (fields === undefined) {
|
|
490
|
+
diagnostics.push(
|
|
491
|
+
`${origin}:官方 Config schema 的模型条目字段集不可推导;` +
|
|
492
|
+
`继承字段回退已知键 ${FALLBACK_MODEL_ENTRY_FIELDS.join('/')}(官方新增字段可能不被继承)`,
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
const generatedAt = catalogGeneratedAtOf(options.catalogModule)
|
|
496
|
+
return {
|
|
497
|
+
protocols: derivedProtocols ?? fallbackProtocols(),
|
|
498
|
+
protocolSource: derivedProtocols === undefined ? 'fallback' : 'official',
|
|
499
|
+
officialModelFields: fields ?? [...FALLBACK_MODEL_ENTRY_FIELDS],
|
|
500
|
+
...(generatedAt === undefined ? {} : { catalogGeneratedAt: generatedAt }),
|
|
501
|
+
}
|
|
336
502
|
}
|
|
337
503
|
|
|
338
|
-
function kitFromTree(
|
|
339
|
-
mods: TreeModules
|
|
340
|
-
auth: Record<string, unknown> | undefined
|
|
341
|
-
config: Record<string, unknown> | undefined
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
504
|
+
function kitFromTree(options: {
|
|
505
|
+
mods: TreeModules
|
|
506
|
+
auth: Record<string, unknown> | undefined
|
|
507
|
+
config: Record<string, unknown> | undefined
|
|
508
|
+
surface: OfficialSurface
|
|
509
|
+
protocols: string[]
|
|
510
|
+
protocolFactories: DshKit['protocolFactories']
|
|
511
|
+
root: string
|
|
512
|
+
versions: KitVersions
|
|
513
|
+
}): DshKit {
|
|
514
|
+
const { mods, surface, protocols, protocolFactories, root, versions } = options
|
|
515
|
+
const resolverDeps = treeResolverDeps(mods, protocolFactories, protocols)
|
|
516
|
+
const officialResolveProfiles = options.config?.['resolveProfiles']
|
|
517
|
+
const officialCredentialStoreFrom = options.auth?.['credentialStoreFrom']
|
|
518
|
+
const officialAuthContextFrom = options.auth?.['authContextFrom']
|
|
348
519
|
const recordKeyFor = mods.piAiAdapter['recordKeyFor'] as (providerId: string) => CredentialKey
|
|
349
520
|
return {
|
|
350
521
|
source: 'dsh-tree',
|
|
522
|
+
root,
|
|
523
|
+
versions,
|
|
524
|
+
protocols,
|
|
525
|
+
protocolSource: surface.protocolSource,
|
|
526
|
+
protocolFactories,
|
|
527
|
+
officialModelFields: surface.officialModelFields,
|
|
528
|
+
...(surface.catalogGeneratedAt === undefined
|
|
529
|
+
? {}
|
|
530
|
+
: { catalogGeneratedAt: surface.catalogGeneratedAt }),
|
|
351
531
|
PiAiAdapter: mods.piAiAdapter['PiAiAdapter'] as DshKit['PiAiAdapter'],
|
|
352
532
|
LlmError: mods.llm['LlmError'] as DshKit['LlmError'],
|
|
353
533
|
resolveRetryPolicy: mods.llm['resolveRetryPolicy'] as DshKit['resolveRetryPolicy'],
|
|
@@ -359,7 +539,6 @@ function kitFromTree(
|
|
|
359
539
|
builtinProviders: mods.catalog['builtinProviders'] as DshKit['builtinProviders'],
|
|
360
540
|
getBuiltinProviders: mods.catalog['getBuiltinProviders'] as DshKit['getBuiltinProviders'],
|
|
361
541
|
getBuiltinModels: mods.catalog['getBuiltinModels'] as DshKit['getBuiltinModels'],
|
|
362
|
-
protocolFactories,
|
|
363
542
|
resolveProfiles:
|
|
364
543
|
officialResolveProfiles !== undefined
|
|
365
544
|
? (officialResolveProfiles as ResolveProfiles)
|
|
@@ -399,27 +578,53 @@ function vendoredAdapterBundlePath(): string {
|
|
|
399
578
|
return fileURLToPath(import.meta.resolve('@deepseek-ai/dsh-llm-pi-ai'))
|
|
400
579
|
}
|
|
401
580
|
|
|
581
|
+
/** vendored 副本的静态协议工厂表(devDependencies 钉版,故无需动态加载)。 */
|
|
582
|
+
function vendoredProtocolFactories(): Record<string, () => unknown> {
|
|
583
|
+
return {
|
|
584
|
+
'openai-completions': vendoredCompletions,
|
|
585
|
+
'openai-responses': vendoredResponses,
|
|
586
|
+
'anthropic-messages': vendoredAnthropic,
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
402
590
|
/**
|
|
403
591
|
* vendored 兜底副本套件(导出供单测直接使用,免走 dsh 树解析)。
|
|
592
|
+
* 副本是**钉版**的,故协议工厂走静态导入,协议集合取"官方声明 ∩ 静态工厂";
|
|
404
593
|
* npm 发布形态不携带 src/、lib 仅 index.js/invariant.js——resolveProfiles 与
|
|
405
594
|
* auth 助手在此无条件走插件等价实现(内联,见文件头说明);compat 门控表则
|
|
406
595
|
* 仍从 vendored 副本的 bundle + Config schema 现场推导(与 dsh 树同一推导链)。
|
|
596
|
+
* @param diagnostics 可选诊断收集器(回退原因与逐项降级说明)。
|
|
407
597
|
*/
|
|
408
|
-
export function loadVendoredKit(): DshKit {
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
598
|
+
export function loadVendoredKit(diagnostics: string[] = []): DshKit {
|
|
599
|
+
const staticFactories = vendoredProtocolFactories()
|
|
600
|
+
const surface = installOfficialSurface({
|
|
601
|
+
bundlePath: vendoredAdapterBundlePath(),
|
|
602
|
+
adapterModule: vendoredPiAiAdapter as unknown as Record<string, unknown>,
|
|
603
|
+
catalogModule: vendoredCatalog as unknown as Record<string, unknown>,
|
|
604
|
+
origin: 'vendored 副本',
|
|
605
|
+
diagnostics,
|
|
606
|
+
})
|
|
607
|
+
const protocols = surface.protocols.filter((api) => staticFactories[api] !== undefined)
|
|
608
|
+
const protocolFactories = protocolFactoriesOf(protocols, staticFactories)
|
|
609
|
+
const resolverDeps: ResolverDeps = {
|
|
610
|
+
createProvider: vendoredPiAi.createProvider,
|
|
611
|
+
protocolFactories,
|
|
612
|
+
protocols,
|
|
613
|
+
resolveRetryPolicy: vendoredLlm.resolveRetryPolicy,
|
|
414
614
|
}
|
|
415
|
-
|
|
416
|
-
installOfficialCompatTable(
|
|
417
|
-
vendoredAdapterBundlePath(),
|
|
418
|
-
vendoredPiAiAdapter as unknown as Record<string, unknown>,
|
|
419
|
-
[],
|
|
420
|
-
)
|
|
615
|
+
const deepseek = loadVendoredDeepseek()
|
|
421
616
|
const kit: DshKit = {
|
|
422
617
|
source: 'vendored',
|
|
618
|
+
versions: collectVendoredVersions(),
|
|
619
|
+
protocols,
|
|
620
|
+
// 官方声明里若有本副本未携带工厂的协议(副本钉版),集合仍是"官方声明"的
|
|
621
|
+
// 子集,故来源标注跟随推导结果;一个都不剩时按回退处理(assertKitShape 兜底)。
|
|
622
|
+
protocolSource: protocols.length > 0 ? surface.protocolSource : 'fallback',
|
|
623
|
+
protocolFactories,
|
|
624
|
+
officialModelFields: surface.officialModelFields,
|
|
625
|
+
...(surface.catalogGeneratedAt === undefined
|
|
626
|
+
? {}
|
|
627
|
+
: { catalogGeneratedAt: surface.catalogGeneratedAt }),
|
|
423
628
|
PiAiAdapter: vendoredPiAiAdapter.PiAiAdapter,
|
|
424
629
|
LlmError: vendoredLlm.LlmError,
|
|
425
630
|
resolveRetryPolicy: vendoredLlm.resolveRetryPolicy,
|
|
@@ -431,13 +636,7 @@ export function loadVendoredKit(): DshKit {
|
|
|
431
636
|
builtinProviders: vendoredCatalog.builtinProviders,
|
|
432
637
|
getBuiltinProviders: vendoredCatalog.getBuiltinProviders,
|
|
433
638
|
getBuiltinModels: vendoredCatalog.getBuiltinModels,
|
|
434
|
-
|
|
435
|
-
resolveProfiles: (providers) =>
|
|
436
|
-
resolveProfilesFallback(providers, {
|
|
437
|
-
createProvider: vendoredPiAi.createProvider,
|
|
438
|
-
protocolFactories,
|
|
439
|
-
resolveRetryPolicy: vendoredLlm.resolveRetryPolicy,
|
|
440
|
-
}),
|
|
639
|
+
resolveProfiles: (providers) => resolveProfilesFallback(providers, resolverDeps),
|
|
441
640
|
auth: {
|
|
442
641
|
credentialStoreFrom: (ctx) =>
|
|
443
642
|
inlineCredentialStoreFrom(ctx, vendoredPiAiAdapter.recordKeyFor),
|
|
@@ -475,7 +674,7 @@ export function dshTreeAnchor(
|
|
|
475
674
|
/**
|
|
476
675
|
* 解析运行时套件:优先 dsh 安装树(自动跟随上游),失败回退 vendored 副本;
|
|
477
676
|
* 两者都过不了形状自检时抛错(调用方应记日志并放弃注册 route)。
|
|
478
|
-
* 返回的 diagnostics
|
|
677
|
+
* 返回的 diagnostics 记录回退原因与逐项降级说明,供配置卡片与日志展示。
|
|
479
678
|
* @param installAnchor profile 的 dsh 安装锚点(`ctx.profileContext.installAnchor`)。
|
|
480
679
|
*/
|
|
481
680
|
export async function resolveDshKit(installAnchor?: string | undefined): Promise<{
|
|
@@ -488,13 +687,33 @@ export async function resolveDshKit(installAnchor?: string | undefined): Promise
|
|
|
488
687
|
const treePkgDir = join(anchor, 'node_modules', '@deepseek-ai', 'dsh-llm-pi-ai')
|
|
489
688
|
try {
|
|
490
689
|
const mods = await importTreeModules(anchor)
|
|
690
|
+
const surface = installOfficialSurface({
|
|
691
|
+
bundlePath: join(treePkgDir, 'lib', 'index.js'),
|
|
692
|
+
adapterModule: mods.piAiAdapter,
|
|
693
|
+
catalogModule: mods.catalog,
|
|
694
|
+
origin: 'dsh 安装树',
|
|
695
|
+
diagnostics,
|
|
696
|
+
})
|
|
697
|
+
const { protocols, protocolFactories } = await loadTreeProtocols(
|
|
698
|
+
anchor,
|
|
699
|
+
surface.protocols,
|
|
700
|
+
diagnostics,
|
|
701
|
+
)
|
|
491
702
|
const [auth, config] = await Promise.all([
|
|
492
703
|
probeSubmodule(treePkgDir, 'auth'),
|
|
493
704
|
probeSubmodule(treePkgDir, 'config'),
|
|
494
705
|
])
|
|
495
|
-
const kit = kitFromTree(
|
|
706
|
+
const kit = kitFromTree({
|
|
707
|
+
mods,
|
|
708
|
+
auth,
|
|
709
|
+
config,
|
|
710
|
+
surface,
|
|
711
|
+
protocols,
|
|
712
|
+
protocolFactories,
|
|
713
|
+
root: anchor,
|
|
714
|
+
versions: collectTreeVersions(anchor),
|
|
715
|
+
})
|
|
496
716
|
assertKitShape(kit, 'dsh-tree')
|
|
497
|
-
installOfficialCompatTable(join(treePkgDir, 'lib', 'index.js'), mods.piAiAdapter, diagnostics)
|
|
498
717
|
if (auth === undefined) {
|
|
499
718
|
diagnostics.push(
|
|
500
719
|
'dsh 树不含 dsh-llm-pi-ai/src(npm 发布形态);认证助手使用插件内联等价实现',
|
|
@@ -524,7 +743,7 @@ export async function resolveDshKit(installAnchor?: string | undefined): Promise
|
|
|
524
743
|
'未能从 profileContext.installAnchor / process.argv[1] 定位 dsh 安装树;回退 vendored 副本',
|
|
525
744
|
)
|
|
526
745
|
}
|
|
527
|
-
const kit = loadVendoredKit()
|
|
746
|
+
const kit = loadVendoredKit(diagnostics)
|
|
528
747
|
if (kit.deepseek === undefined) {
|
|
529
748
|
diagnostics.push(
|
|
530
749
|
'vendored 副本的 dsh-llm-deepseek 形状不兼容;adapter: deepseek 的 route 不可用',
|