@dsh-plus/llm-pi 0.2.0 → 0.2.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/lib/client.js +33 -13
- package/lib/index.d.ts +2 -2
- package/lib/index.js +140 -47
- package/package.json +2 -2
- package/src/auth-inline.ts +21 -8
- package/src/client/api.ts +8 -1
- package/src/client/client.ts +2 -2
- package/src/client/i18n.ts +4 -2
- package/src/client/root-fields.tsx +29 -5
- package/src/compat.ts +2 -2
- package/src/config-api.ts +1 -1
- package/src/config.ts +6 -17
- package/src/deepseek-routes.ts +2 -2
- package/src/inherit.ts +1 -1
- package/src/ns.ts +1 -1
- package/src/official-surface.ts +45 -4
- package/src/profiles-deepseek.ts +1 -11
- package/src/profiles.ts +2 -10
- package/src/resolve-dsh.ts +110 -46
- package/src/service.ts +20 -14
package/src/resolve-dsh.ts
CHANGED
|
@@ -19,13 +19,15 @@
|
|
|
19
19
|
* 模型条目字段集同样现场推导(official-surface.ts)。生效版本只用于状态行显示,
|
|
20
20
|
* 超出已验证区间也只是提示(kit-meta.ts),永不阻断。
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
* - resolveProfiles(config.ts
|
|
22
|
+
* 仅 src 子路径导出、包根不导出的两块面:
|
|
23
|
+
* - resolveProfiles(config.ts):官方解析链,源码布局可经 src 子路径复用;
|
|
24
24
|
* - credentialStoreFrom/authContextFrom(auth.ts):PiAiAdapter 必需 auth 注入。
|
|
25
25
|
* npm 发布形态(lib/index.js 单 bundle + 无 src/)两者都拿不到:dsh 树优先
|
|
26
26
|
* 探测 src/*(含 lib/* 候选),未命中时 resolveProfiles 走插件等价实现
|
|
27
|
-
* (profiles.ts
|
|
28
|
-
* (auth-inline.ts,recordKeyFor
|
|
27
|
+
* (profiles.ts,语义对齐官方 config.ts:resolveProfiles)、auth 走内联等价实现
|
|
28
|
+
* (auth-inline.ts,recordKeyFor 自包根导出同源注入、记录作用域由它现场推导)。
|
|
29
|
+
* 这是**打包形态的恒定事实而非降级**,故按 info 档记入诊断(missingSourceNote),
|
|
30
|
+
* 免得淹没同面板里真正的降级;等价实现由形状自检与单测兜底。
|
|
29
31
|
* @module llm-pi/resolve-dsh
|
|
30
32
|
*/
|
|
31
33
|
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
@@ -76,7 +78,7 @@ import { type ResolverDeps, resolveProfilesFallback } from './profiles.ts'
|
|
|
76
78
|
|
|
77
79
|
/**
|
|
78
80
|
* deepseek 适配器模块表面(可选):dsh-llm-deepseek 的官方 DeepSeekAdapter
|
|
79
|
-
* 与目录解析器 + 匿名用户 id
|
|
81
|
+
* 与目录解析器 + 匿名用户 id。缺失(形状漂移)不拖垮核心
|
|
80
82
|
* 套件——deepseek 类 route 在构建期以明确错误拒绝,pi 路由不受影响。
|
|
81
83
|
* 与核心套件强制同源(杜绝跨源模块混用:brand/LlmError 恒等性敏感)。
|
|
82
84
|
*/
|
|
@@ -106,6 +108,43 @@ export interface AuthHelpers {
|
|
|
106
108
|
authContextFrom(ctx: Context): AuthContext
|
|
107
109
|
}
|
|
108
110
|
|
|
111
|
+
/**
|
|
112
|
+
* 套件诊断条目,配置页按档分块展示:
|
|
113
|
+
* - `info`:当前打包形态下的**恒定事实**,不是缺陷——官方 `src/` 只经子路径导出、
|
|
114
|
+
* npm 发布形态不携带,本插件因此走等价实现(见 {@link missingSourceNote});
|
|
115
|
+
* - `degradation`:推导/加载真的降级了(回退快照、协议缺失、树不可用等)。
|
|
116
|
+
* 分档是为了不让恒定事实淹没真实降级。
|
|
117
|
+
*/
|
|
118
|
+
export interface KitDiagnostic {
|
|
119
|
+
level: 'info' | 'degradation'
|
|
120
|
+
message: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** 生成一条降级诊断(推导失败 / 加载回退)。 */
|
|
124
|
+
export function degradation(message: string): KitDiagnostic {
|
|
125
|
+
return { level: 'degradation', message }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 官方 `src/` 子路径未随 npm 发布携带时的**形态说明**(info 档)。
|
|
130
|
+
*
|
|
131
|
+
* 官方 `resolveProfiles`(src/config.ts)与认证助手(src/auth.ts)只在子路径导出、
|
|
132
|
+
* 包根不导出,而发布清单只含 `lib/index.js` + `lib/types/**`——任何 npm 安装形态都
|
|
133
|
+
* 拿不到,故这两块常走插件等价实现。此处逐面列出实际走等价实现的部分,并点明
|
|
134
|
+
* **仍取自官方现场推导**的部分(compat 门控 / 协议 / 模型字段集),避免被读成
|
|
135
|
+
* "自动跟随失败"。
|
|
136
|
+
* @param surfaces 走等价实现的官方面名称(如 `profile 解析`)。
|
|
137
|
+
*/
|
|
138
|
+
export function missingSourceNote(surfaces: readonly string[]): KitDiagnostic {
|
|
139
|
+
return {
|
|
140
|
+
level: 'info',
|
|
141
|
+
message:
|
|
142
|
+
'官方 @deepseek-ai/dsh-llm-pi-ai 只经 src/ 子路径导出、npm 发布形态不携带 src/:' +
|
|
143
|
+
`${surfaces.join('、')}使用插件等价实现;` +
|
|
144
|
+
'compat 门控 / 协议集合 / 模型条目字段集仍取自官方安装副本现场推导',
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
109
148
|
/** 插件运行期所需的全部上游模块表面(单一来源,内部一致)。 */
|
|
110
149
|
export interface DshKit {
|
|
111
150
|
/** 解析来源:dsh 安装树 / 插件 vendored 兜底副本。 */
|
|
@@ -140,16 +179,22 @@ export interface DshKit {
|
|
|
140
179
|
getBuiltinProviders: typeof vendoredCatalog.getBuiltinProviders
|
|
141
180
|
getBuiltinModels: typeof vendoredCatalog.getBuiltinModels
|
|
142
181
|
/**
|
|
143
|
-
* profile
|
|
144
|
-
* resolveProfiles;npm
|
|
182
|
+
* profile 解析链:官方源码布局(`src/config.ts` 存在,如上游工作区)时为官方
|
|
183
|
+
* resolveProfiles;npm 发布形态(不携带 src/,包根也不导出)与 vendored 兜底时
|
|
184
|
+
* 为插件等价实现——该形态事实按 info 档记入诊断(见 {@link missingSourceNote})。
|
|
145
185
|
*/
|
|
146
186
|
resolveProfiles: ResolveProfiles
|
|
147
187
|
/**
|
|
148
|
-
* PiAiAdapter 必需 auth
|
|
188
|
+
* PiAiAdapter 必需 auth 注入:官方源码布局(`src/auth.ts` 存在)时为官方
|
|
149
189
|
* credentialStoreFrom/authContextFrom;其余形态为内联等价实现
|
|
150
|
-
* (auth-inline.ts
|
|
190
|
+
* (auth-inline.ts,记录键与作用域均取自官方 recordKeyFor)。
|
|
151
191
|
*/
|
|
152
192
|
auth: AuthHelpers
|
|
193
|
+
/**
|
|
194
|
+
* 官方凭据记录键格式器(包根导出)。auth 助手按它对齐记录键,并**现场推导**
|
|
195
|
+
* 记录作用域(不再手抄官方常量,见 auth-inline.ts);缺失即形状不兼容。
|
|
196
|
+
*/
|
|
197
|
+
recordKeyFor: (providerId: string) => CredentialKey
|
|
153
198
|
/** 附件引用 → 当前模型工具执行世界的只读路径桥(官方 dsh-llm 导出)。 */
|
|
154
199
|
resolveImageAttachmentAccess: typeof DshLlm.resolveImageAttachmentAccess
|
|
155
200
|
/** deepseek 文件通道路由所需模块;同源自检失败时为 undefined(见 diagnostics)。 */
|
|
@@ -161,8 +206,8 @@ function assertKitShape(kit: DshKit, origin: string): void {
|
|
|
161
206
|
const problems: string[] = []
|
|
162
207
|
if (typeof kit.PiAiAdapter !== 'function') problems.push('PiAiAdapter 不是类')
|
|
163
208
|
else {
|
|
164
|
-
//
|
|
165
|
-
//
|
|
209
|
+
// LlmAdapter 抽象面:prepareCall/providerRetryPolicy 为适配器必需 override;
|
|
210
|
+
// models.getModels/getModel 语义
|
|
166
211
|
// (pi-ai 0.84.2 Models.getModel(provider,id)/getModels(provider),
|
|
167
212
|
// 由同源 adapter 内部消费,本插件不直调)。
|
|
168
213
|
for (const method of [
|
|
@@ -207,6 +252,7 @@ function assertKitShape(kit: DshKit, origin: string): void {
|
|
|
207
252
|
) {
|
|
208
253
|
problems.push('auth 助手(credentialStoreFrom/authContextFrom)缺失')
|
|
209
254
|
}
|
|
255
|
+
if (typeof kit.recordKeyFor !== 'function') problems.push('recordKeyFor 缺失')
|
|
210
256
|
if (problems.length > 0) {
|
|
211
257
|
throw new Error(`llm-pi: ${origin} 来源的运行时套件形状不兼容:${problems.join(';')}`)
|
|
212
258
|
}
|
|
@@ -343,7 +389,7 @@ function piAiApiDir(root: string): string {
|
|
|
343
389
|
/** 内置三元组的静态加载(协议推导失败或全部工厂加载失败时的兜底)。 */
|
|
344
390
|
async function loadFallbackTreeFactories(
|
|
345
391
|
root: string,
|
|
346
|
-
diagnostics:
|
|
392
|
+
diagnostics: KitDiagnostic[],
|
|
347
393
|
): Promise<Record<string, () => unknown>> {
|
|
348
394
|
const dir = piAiApiDir(root)
|
|
349
395
|
const out: Record<string, () => unknown> = {}
|
|
@@ -359,8 +405,10 @@ async function loadFallbackTreeFactories(
|
|
|
359
405
|
}
|
|
360
406
|
if (Object.keys(out).length < fallbackProtocols().length) {
|
|
361
407
|
diagnostics.push(
|
|
362
|
-
|
|
363
|
-
|
|
408
|
+
degradation(
|
|
409
|
+
`pi-ai 协议兜底三元组未能全部加载(${Object.keys(out).join('/') || '空'});` +
|
|
410
|
+
'请检查 dsh 安装树里 @earendil-works/pi-ai/dist/api 的形态',
|
|
411
|
+
),
|
|
364
412
|
)
|
|
365
413
|
}
|
|
366
414
|
return out
|
|
@@ -373,7 +421,7 @@ async function loadFallbackTreeFactories(
|
|
|
373
421
|
async function loadTreeProtocols(
|
|
374
422
|
root: string,
|
|
375
423
|
derived: string[],
|
|
376
|
-
diagnostics:
|
|
424
|
+
diagnostics: KitDiagnostic[],
|
|
377
425
|
): Promise<{ protocols: string[]; protocolFactories: Record<string, () => unknown> }> {
|
|
378
426
|
const dir = piAiApiDir(root)
|
|
379
427
|
const out: Record<string, () => unknown> = {}
|
|
@@ -381,27 +429,31 @@ async function loadTreeProtocols(
|
|
|
381
429
|
const file = join(dir, `${api}.lazy.js`)
|
|
382
430
|
if (!existsSync(file)) {
|
|
383
431
|
diagnostics.push(
|
|
384
|
-
|
|
432
|
+
degradation(
|
|
433
|
+
`pi-ai 未提供协议 "${api}" 的惰性实现(缺 ${file});该协议不可用,其余协议不受影响`,
|
|
434
|
+
),
|
|
385
435
|
)
|
|
386
436
|
continue
|
|
387
437
|
}
|
|
388
438
|
try {
|
|
389
439
|
const factory = protocolFactoryOf((await import(pathToFileURL(file).href)) as never)
|
|
390
440
|
if (factory === undefined) {
|
|
391
|
-
diagnostics.push(`协议 "${api}" 的实现模块未导出唯一 *Api 工厂;该协议不可用`)
|
|
441
|
+
diagnostics.push(degradation(`协议 "${api}" 的实现模块未导出唯一 *Api 工厂;该协议不可用`))
|
|
392
442
|
continue
|
|
393
443
|
}
|
|
394
444
|
out[api] = factory
|
|
395
445
|
} catch (error) {
|
|
396
446
|
diagnostics.push(
|
|
397
|
-
|
|
447
|
+
degradation(
|
|
448
|
+
`协议 "${api}" 的实现加载失败(${error instanceof Error ? error.message : String(error)});该协议不可用`,
|
|
449
|
+
),
|
|
398
450
|
)
|
|
399
451
|
}
|
|
400
452
|
}
|
|
401
453
|
if (Object.keys(out).length > 0) {
|
|
402
454
|
return { protocols: derived.filter((api) => out[api] !== undefined), protocolFactories: out }
|
|
403
455
|
}
|
|
404
|
-
diagnostics.push('官方声明的协议一个都加载不到;回退内置三元组')
|
|
456
|
+
diagnostics.push(degradation('官方声明的协议一个都加载不到;回退内置三元组'))
|
|
405
457
|
const fallback = await loadFallbackTreeFactories(root, diagnostics)
|
|
406
458
|
return {
|
|
407
459
|
protocols: fallbackProtocols().filter((api) => fallback[api] !== undefined),
|
|
@@ -465,7 +517,7 @@ function installOfficialSurface(options: {
|
|
|
465
517
|
adapterModule: Record<string, unknown>
|
|
466
518
|
catalogModule: Record<string, unknown>
|
|
467
519
|
origin: string
|
|
468
|
-
diagnostics:
|
|
520
|
+
diagnostics: KitDiagnostic[]
|
|
469
521
|
}): OfficialSurface {
|
|
470
522
|
const { bundlePath, adapterModule, origin, diagnostics } = options
|
|
471
523
|
const compatLabel = 'compat 门控表'
|
|
@@ -477,8 +529,10 @@ function installOfficialSurface(options: {
|
|
|
477
529
|
// 快照模式下未知 compat 键**放行**(交给官方自身校验),只拦快照里明确的
|
|
478
530
|
// withhold——表落后时"误拒官方新字段"比"多放一个键"严重得多。
|
|
479
531
|
diagnostics.push(
|
|
480
|
-
|
|
481
|
-
|
|
532
|
+
degradation(
|
|
533
|
+
`${compatLabel}未能从官方 bundle 解析 COMPAT_GATES(${bundlePath});` +
|
|
534
|
+
`使用内置快照(未知字段放行、withhold 仍拒绝;请检查 dsh-llm-pi-ai 打包形态)`,
|
|
535
|
+
),
|
|
482
536
|
)
|
|
483
537
|
installCompatTable(FALLBACK_TABLE)
|
|
484
538
|
} else {
|
|
@@ -488,21 +542,27 @@ function installOfficialSurface(options: {
|
|
|
488
542
|
}
|
|
489
543
|
} catch (error) {
|
|
490
544
|
const reason = error instanceof Error ? error.message : String(error)
|
|
491
|
-
diagnostics.push(
|
|
545
|
+
diagnostics.push(
|
|
546
|
+
degradation(`${compatLabel}推导失败(${reason});使用内置快照(未知字段放行)`),
|
|
547
|
+
)
|
|
492
548
|
installCompatTable(FALLBACK_TABLE)
|
|
493
549
|
}
|
|
494
550
|
|
|
495
551
|
const derivedProtocols = deriveProtocols(adapterModule)
|
|
496
552
|
if (derivedProtocols === undefined) {
|
|
497
553
|
diagnostics.push(
|
|
498
|
-
|
|
554
|
+
degradation(
|
|
555
|
+
`${origin}:官方 supportedProtocols() 不可用;协议集合回退内置三元组 ${fallbackProtocols().join('/')}`,
|
|
556
|
+
),
|
|
499
557
|
)
|
|
500
558
|
}
|
|
501
559
|
const fields = deriveModelEntryFields(adapterModule)
|
|
502
560
|
if (fields === undefined) {
|
|
503
561
|
diagnostics.push(
|
|
504
|
-
|
|
505
|
-
|
|
562
|
+
degradation(
|
|
563
|
+
`${origin}:官方 Config schema 的模型条目字段集不可推导;` +
|
|
564
|
+
`继承字段回退已知键 ${FALLBACK_MODEL_ENTRY_FIELDS.join('/')}(官方新增字段可能不被继承)`,
|
|
565
|
+
),
|
|
506
566
|
)
|
|
507
567
|
}
|
|
508
568
|
const generatedAt = catalogGeneratedAtOf(options.catalogModule)
|
|
@@ -570,6 +630,7 @@ function kitFromTree(options: {
|
|
|
570
630
|
resolveImageAttachmentAccess: mods.llm[
|
|
571
631
|
'resolveImageAttachmentAccess'
|
|
572
632
|
] as DshKit['resolveImageAttachmentAccess'],
|
|
633
|
+
recordKeyFor,
|
|
573
634
|
}
|
|
574
635
|
}
|
|
575
636
|
|
|
@@ -607,9 +668,9 @@ function vendoredProtocolFactories(): Record<string, () => unknown> {
|
|
|
607
668
|
* npm 发布形态不携带 src/、lib 仅 index.js/invariant.js——resolveProfiles 与
|
|
608
669
|
* auth 助手在此无条件走插件等价实现(内联,见文件头说明);compat 门控表则
|
|
609
670
|
* 仍从 vendored 副本的 bundle + Config schema 现场推导(与 dsh 树同一推导链)。
|
|
610
|
-
* @param diagnostics
|
|
671
|
+
* @param diagnostics 可选诊断收集器(回退原因、形态说明与逐项降级)。
|
|
611
672
|
*/
|
|
612
|
-
export function loadVendoredKit(diagnostics:
|
|
673
|
+
export function loadVendoredKit(diagnostics: KitDiagnostic[] = []): DshKit {
|
|
613
674
|
const staticFactories = vendoredProtocolFactories()
|
|
614
675
|
const surface = installOfficialSurface({
|
|
615
676
|
bundlePath: vendoredAdapterBundlePath(),
|
|
@@ -658,9 +719,11 @@ export function loadVendoredKit(diagnostics: string[] = []): DshKit {
|
|
|
658
719
|
authContextFrom: inlineAuthContextFrom,
|
|
659
720
|
},
|
|
660
721
|
resolveImageAttachmentAccess: vendoredLlm.resolveImageAttachmentAccess,
|
|
722
|
+
recordKeyFor: vendoredPiAiAdapter.recordKeyFor,
|
|
661
723
|
...(deepseek === undefined ? {} : { deepseek }),
|
|
662
724
|
}
|
|
663
725
|
assertKitShape(kit, 'vendored')
|
|
726
|
+
diagnostics.push(missingSourceNote(['profile 解析', '认证助手']))
|
|
664
727
|
return kit
|
|
665
728
|
}
|
|
666
729
|
|
|
@@ -689,14 +752,15 @@ export function dshTreeAnchor(
|
|
|
689
752
|
/**
|
|
690
753
|
* 解析运行时套件:优先 dsh 安装树(自动跟随上游),失败回退 vendored 副本;
|
|
691
754
|
* 两者都过不了形状自检时抛错(调用方应记日志并放弃注册 route)。
|
|
692
|
-
* 返回的 diagnostics
|
|
755
|
+
* 返回的 diagnostics 记录回退原因、打包形态说明(info)与逐项降级(degradation),
|
|
756
|
+
* 供配置卡片与日志分档展示。
|
|
693
757
|
* @param installAnchor profile 的 dsh 安装锚点(`ctx.profileContext.installAnchor`)。
|
|
694
758
|
*/
|
|
695
759
|
export async function resolveDshKit(installAnchor?: string | undefined): Promise<{
|
|
696
760
|
kit: DshKit
|
|
697
|
-
diagnostics:
|
|
761
|
+
diagnostics: KitDiagnostic[]
|
|
698
762
|
}> {
|
|
699
|
-
const diagnostics:
|
|
763
|
+
const diagnostics: KitDiagnostic[] = []
|
|
700
764
|
const anchor = dshTreeAnchor(installAnchor)
|
|
701
765
|
if (anchor !== undefined) {
|
|
702
766
|
const treePkgDir = join(anchor, 'node_modules', '@deepseek-ai', 'dsh-llm-pi-ai')
|
|
@@ -729,39 +793,39 @@ export async function resolveDshKit(installAnchor?: string | undefined): Promise
|
|
|
729
793
|
versions: collectTreeVersions(anchor),
|
|
730
794
|
})
|
|
731
795
|
assertKitShape(kit, 'dsh-tree')
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
if (config === undefined) {
|
|
738
|
-
diagnostics.push(
|
|
739
|
-
'dsh 树不含 dsh-llm-pi-ai/src(npm 发布形态);profile 解析使用插件等价实现(compat 门控对齐官方 catalog.ts)',
|
|
740
|
-
)
|
|
741
|
-
}
|
|
796
|
+
const missingSeams: string[] = []
|
|
797
|
+
if (config === undefined) missingSeams.push('profile 解析')
|
|
798
|
+
if (auth === undefined) missingSeams.push('认证助手')
|
|
799
|
+
if (missingSeams.length > 0) diagnostics.push(missingSourceNote(missingSeams))
|
|
742
800
|
const tree = await importTreeDeepseek(anchor)
|
|
743
801
|
if (tree.kit !== undefined) {
|
|
744
802
|
return { kit: { ...kit, deepseek: tree.kit }, diagnostics }
|
|
745
803
|
}
|
|
746
804
|
diagnostics.push(
|
|
747
|
-
|
|
748
|
-
|
|
805
|
+
degradation(
|
|
806
|
+
`dsh 安装树的 dsh-llm-deepseek 不可用(${tree.problem ?? '未知原因'});` +
|
|
807
|
+
'adapter: deepseek 的 route 不可用,pi 路由不受影响',
|
|
808
|
+
),
|
|
749
809
|
)
|
|
750
810
|
return { kit, diagnostics }
|
|
751
811
|
} catch (error) {
|
|
752
812
|
diagnostics.push(
|
|
753
|
-
|
|
813
|
+
degradation(
|
|
814
|
+
`dsh 安装树套件不可用(${anchor}):${error instanceof Error ? error.message : String(error)};回退 vendored 副本`,
|
|
815
|
+
),
|
|
754
816
|
)
|
|
755
817
|
}
|
|
756
818
|
} else {
|
|
757
819
|
diagnostics.push(
|
|
758
|
-
|
|
820
|
+
degradation(
|
|
821
|
+
'未能从 profileContext.installAnchor / process.argv[1] 定位 dsh 安装树;回退 vendored 副本',
|
|
822
|
+
),
|
|
759
823
|
)
|
|
760
824
|
}
|
|
761
825
|
const kit = loadVendoredKit(diagnostics)
|
|
762
826
|
if (kit.deepseek === undefined) {
|
|
763
827
|
diagnostics.push(
|
|
764
|
-
'vendored 副本的 dsh-llm-deepseek 形状不兼容;adapter: deepseek 的 route 不可用',
|
|
828
|
+
degradation('vendored 副本的 dsh-llm-deepseek 形状不兼容;adapter: deepseek 的 route 不可用'),
|
|
765
829
|
)
|
|
766
830
|
}
|
|
767
831
|
return { kit, diagnostics }
|
package/src/service.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 运行时主逻辑:注册/热更新/发现(逐点对齐官方 dsh-llm-pi-ai apply 的模式)。
|
|
3
3
|
*
|
|
4
|
-
* - profiles 回调按原始 config 对象 identity 备忘;配置变更经
|
|
5
|
-
*
|
|
4
|
+
* - profiles 回调按原始 config 对象 identity 备忘;配置变更经 volatile 原位提交
|
|
5
|
+
* (loader/volatile-update 换代快照)传播,下一请求生效
|
|
6
6
|
* (adapter 快照按 profiles identity 失效);
|
|
7
7
|
* - route 集或注册时捕获的事实(displayName/retryPolicy)变化 → 原子的
|
|
8
8
|
* handle.replace 重注册;写入被校验拒绝时保留旧注册(官方同款护栏);
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
} from './official-copy-writer.ts'
|
|
48
48
|
import { assertServiceable, buildProfiles, isDraftRoute } from './profiles.ts'
|
|
49
49
|
import { buildDeepseekRoutes, type ResolvedDeepseekRoute } from './profiles-deepseek.ts'
|
|
50
|
-
import { type DshKit, resolveDshKit } from './resolve-dsh.ts'
|
|
50
|
+
import { type DshKit, degradation, type KitDiagnostic, resolveDshKit } from './resolve-dsh.ts'
|
|
51
51
|
|
|
52
52
|
/** 配置页「运行期状态」所需的事实(纯 JSON,可直接进 HTTP 响应)。 */
|
|
53
53
|
export interface LlmPiKitInfo {
|
|
@@ -71,8 +71,11 @@ export interface LlmPiKitInfo {
|
|
|
71
71
|
compatSource: string
|
|
72
72
|
/** 官方应急副本状态(路径/生成时间/覆盖 route/警告;error = 最近生成失败)。 */
|
|
73
73
|
officialCopy: OfficialCopyStatus
|
|
74
|
-
/**
|
|
75
|
-
|
|
74
|
+
/**
|
|
75
|
+
* 套件诊断(分档,界面分块展示):`info` = 打包形态说明(恒定事实、非缺陷),
|
|
76
|
+
* `degradation` = 回退与逐项降级。
|
|
77
|
+
*/
|
|
78
|
+
diagnostics: KitDiagnostic[]
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
export interface LlmPiRuntime {
|
|
@@ -132,13 +135,16 @@ function makeResolveApiKey(ctx: Context, kit: DshKit) {
|
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
137
|
|
|
135
|
-
/**
|
|
138
|
+
/** 解析运行期套件并落启动日志(来源/生效版本/协议/形态说明与逐项降级)。 */
|
|
136
139
|
async function loadRuntimeKit(
|
|
137
140
|
ctx: Context,
|
|
138
141
|
logger: PluginLogger,
|
|
139
|
-
): Promise<{ kit: DshKit; diagnostics:
|
|
142
|
+
): Promise<{ kit: DshKit; diagnostics: KitDiagnostic[] }> {
|
|
140
143
|
const { kit, diagnostics } = await resolveDshKit(ctx.get('profileContext')?.installAnchor)
|
|
141
|
-
for (const
|
|
144
|
+
for (const item of diagnostics) {
|
|
145
|
+
if (item.level === 'info') logger.info(item.message)
|
|
146
|
+
else logger.warn(item.message)
|
|
147
|
+
}
|
|
142
148
|
logger.info(
|
|
143
149
|
`运行时套件来源:${kit.source};pi-ai ${kit.versions.piAi ?? '?'} / ` +
|
|
144
150
|
`dsh-llm-pi-ai ${kit.versions.piAiAdapter ?? '?'};协议 ${kit.protocols.join('/')}`,
|
|
@@ -201,8 +207,8 @@ function createAdapter(options: {
|
|
|
201
207
|
profiles,
|
|
202
208
|
resolveApiKey: makeResolveApiKey(ctx, kit),
|
|
203
209
|
resolveAttachments: () => ctx.get('attachments'),
|
|
204
|
-
//
|
|
205
|
-
//
|
|
210
|
+
// 官方 auth 助手只在 src 子路径导出:登录/OAuth 类 provider 与 pi-ai 自有
|
|
211
|
+
// 凭据写入的落点。dsh 树 dev 布局(src/auth.ts 存在)时为官方助手,
|
|
206
212
|
// 其余形态为内联等价实现(resolve-dsh.ts 探测,见 auth-inline.ts)。
|
|
207
213
|
auth: {
|
|
208
214
|
credentials: kit.auth.credentialStoreFrom(ctx),
|
|
@@ -372,7 +378,7 @@ export async function startRuntime(
|
|
|
372
378
|
): Promise<LlmPiRuntime> {
|
|
373
379
|
const logger = ctx.logger('llm-pi')
|
|
374
380
|
// cordis 行级 config 可能未经 schema 解析(insert 行无 config 键时为原始空对象),
|
|
375
|
-
// 在此统一规范化;
|
|
381
|
+
// 在此统一规范化;loader 解析出的 volatile 活动字段形态直接透传
|
|
376
382
|
// (hasVolatileRefs 辨识——重复校验会把引用再包一层、破坏 loader 原位提交)。
|
|
377
383
|
const fields: LlmPiConfigFields = hasVolatileRefs(rawConfig)
|
|
378
384
|
? (rawConfig as LlmPiConfigFields)
|
|
@@ -434,7 +440,7 @@ export async function startRuntime(
|
|
|
434
440
|
ensureDirectory()
|
|
435
441
|
void copyWriter.ensureOfficialCopy()
|
|
436
442
|
|
|
437
|
-
//
|
|
443
|
+
// 替代 installSection(validate/setSource/onChange 三钩子):
|
|
438
444
|
// - 活动引用原位提交 → volatile-update 触发快照换代与重注册(原 onChange 体);
|
|
439
445
|
// - internal/config waterfall 承接写入校验(原 validate;官方 llm-pi-ai
|
|
440
446
|
// 同款):configEditor.edit 落盘前先跑 waterfall,handler throw 即拒绝整笔写入。
|
|
@@ -479,8 +485,8 @@ export async function startRuntime(
|
|
|
479
485
|
officialCopy: copy,
|
|
480
486
|
diagnostics: [
|
|
481
487
|
...diagnostics,
|
|
482
|
-
...copy.warnings.map((warning) => `应急副本:${warning}`),
|
|
483
|
-
...(copy.error === undefined ? [] : [`应急副本:生成失败(${copy.error})`]),
|
|
488
|
+
...copy.warnings.map((warning) => degradation(`应急副本:${warning}`)),
|
|
489
|
+
...(copy.error === undefined ? [] : [degradation(`应急副本:生成失败(${copy.error})`)]),
|
|
484
490
|
],
|
|
485
491
|
}
|
|
486
492
|
}
|