@dsh-plus/llm-pi 0.1.32 → 0.1.34
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 +3 -3
- package/lib/index.d.ts +4 -1
- package/lib/index.js +39 -38
- package/package.json +31 -31
- package/src/client/client.ts +2 -2
- package/src/config.ts +20 -6
- package/src/service.ts +60 -42
package/lib/client.js
CHANGED
|
@@ -42,7 +42,7 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
className: `${p}-pending`,
|
|
43
43
|
children: props.dirtyLabel
|
|
44
44
|
}) : null,
|
|
45
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
45
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
|
|
46
46
|
]
|
|
47
47
|
}), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
48
48
|
className: `${p}-body`,
|
|
@@ -792,7 +792,7 @@ ${extra}
|
|
|
792
792
|
id: props.id,
|
|
793
793
|
"aria-expanded": open,
|
|
794
794
|
onClick: () => setOpen(!open),
|
|
795
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
795
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `lpc-chevron${open ? " lpc-chevronOpen" : ""}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
796
796
|
className: "lpc-collapseTitle",
|
|
797
797
|
children: props.title
|
|
798
798
|
})]
|
|
@@ -1674,7 +1674,7 @@ ${extra}
|
|
|
1674
1674
|
"aria-expanded": open,
|
|
1675
1675
|
onClick: () => setOpen(!open),
|
|
1676
1676
|
children: [
|
|
1677
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1677
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `lpc-chevron${open ? " lpc-chevronOpen" : ""}` }),
|
|
1678
1678
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1679
1679
|
className: "lpc-routeKey",
|
|
1680
1680
|
children: route
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import { VolatileFields } from "@dsh-plus/shared";
|
|
2
3
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
4
|
//#region src/config.d.ts
|
|
4
5
|
/** settings 命名空间(字面量即合法命名空间,0.1.2-alpha.2 起编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
|
|
@@ -92,7 +93,9 @@ interface LlmPiConfig {
|
|
|
92
93
|
* (5 个根字段均有 schema 默认值),解析输出仍是完整的 {@link LlmPiConfig}。
|
|
93
94
|
*/
|
|
94
95
|
type LlmPiConfigInput = Partial<LlmPiConfig>;
|
|
95
|
-
|
|
96
|
+
/** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
|
|
97
|
+
type LlmPiConfigFields = VolatileFields<LlmPiConfig>;
|
|
98
|
+
declare const Config: z<LlmPiConfigInput, LlmPiConfigFields>;
|
|
96
99
|
//#endregion
|
|
97
100
|
//#region src/index.d.ts
|
|
98
101
|
declare const name = "dsh-plus-llm-pi";
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
3
3
|
import { deepEqualJson } from "@deepseek-ai/dsh-util-values";
|
|
4
|
-
import { pluginDataPath } from "@dsh-plus/shared";
|
|
4
|
+
import { hasVolatileRefs, pluginDataPath, unwrapVolatile } from "@dsh-plus/shared";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { request } from "node:http";
|
|
7
7
|
import { request as request$1 } from "node:https";
|
|
@@ -146,11 +146,11 @@ const providerProfile = z.object({
|
|
|
146
146
|
models: z.array(modelEntry).description("本 route 的模型目录;缺省且 provider 有 extends 时继承该源全部模型")
|
|
147
147
|
});
|
|
148
148
|
const Config = z.object({
|
|
149
|
-
enabled: z.boolean().description("总开关(关闭则不注册任何 route)").default(true),
|
|
150
|
-
catalogUrl: z.string().description("models.dev 目录数据端点").default("https://models.dev/api.json"),
|
|
151
|
-
catalogRefreshHours: z.number().description("models.dev 自动拉取间隔小时数;0 = 不自动拉取(可手动拉取或读已有缓存)").default(0),
|
|
152
|
-
catalogProxy: z.string().description("拉取 models.dev 目录时的 HTTP 代理地址(如 http://127.0.0.1:7890);留空直连").default(""),
|
|
153
|
-
providers: z.dict(providerProfile).description("provider 路由表,键即 route 名").default({})
|
|
149
|
+
enabled: z.boolean().description("总开关(关闭则不注册任何 route)").default(true).volatile(),
|
|
150
|
+
catalogUrl: z.string().description("models.dev 目录数据端点").default("https://models.dev/api.json").volatile(),
|
|
151
|
+
catalogRefreshHours: z.number().description("models.dev 自动拉取间隔小时数;0 = 不自动拉取(可手动拉取或读已有缓存)").default(0).volatile(),
|
|
152
|
+
catalogProxy: z.string().description("拉取 models.dev 目录时的 HTTP 代理地址(如 http://127.0.0.1:7890);留空直连").default("").volatile(),
|
|
153
|
+
providers: z.dict(providerProfile).description("provider 路由表,键即 route 名").default({}).volatile()
|
|
154
154
|
});
|
|
155
155
|
//#endregion
|
|
156
156
|
//#region src/catalog/builtin.ts
|
|
@@ -2131,13 +2131,14 @@ function makeResolveApiKey(ctx, kit) {
|
|
|
2131
2131
|
/** 启动插件运行时:解析套件、挂载注册/发现/settings 联动。 */
|
|
2132
2132
|
async function startRuntime(ctx, rawConfig) {
|
|
2133
2133
|
const logger = ctx.logger("llm-pi");
|
|
2134
|
-
const
|
|
2134
|
+
const fields = hasVolatileRefs(rawConfig) ? rawConfig : Config(rawConfig ?? {});
|
|
2135
2135
|
const { kit, diagnostics } = await resolveDshKit();
|
|
2136
2136
|
for (const line of diagnostics) logger.warn(line);
|
|
2137
2137
|
logger.info(`运行时套件来源:${kit.source}`);
|
|
2138
|
-
|
|
2138
|
+
let snapshot = unwrapVolatile(fields);
|
|
2139
|
+
const modelsDev = new ModelsDevSource(pluginDataPath("llm-pi", "models-dev.json"), snapshot.catalogUrl, snapshot.catalogRefreshHours, (message) => logger.warn(message), snapshot.catalogProxy ?? "");
|
|
2139
2140
|
modelsDev.ensureLoaded();
|
|
2140
|
-
|
|
2141
|
+
const current = () => snapshot;
|
|
2141
2142
|
let lastRaw;
|
|
2142
2143
|
let memoized;
|
|
2143
2144
|
let memoizedDeepseek;
|
|
@@ -2283,35 +2284,35 @@ async function startRuntime(ctx, rawConfig) {
|
|
|
2283
2284
|
ensureRegistration();
|
|
2284
2285
|
ensureDeepseek();
|
|
2285
2286
|
ensureDirectory();
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2287
|
+
const reconfigure = () => {
|
|
2288
|
+
snapshot = unwrapVolatile(fields);
|
|
2289
|
+
try {
|
|
2290
|
+
ensureRegistration();
|
|
2291
|
+
} catch (error) {
|
|
2292
|
+
logger.error("llm-pi: 更新被拒,保留此前注册的 route");
|
|
2293
|
+
logger.error(error);
|
|
2294
|
+
}
|
|
2295
|
+
try {
|
|
2296
|
+
ensureDeepseek();
|
|
2297
|
+
} catch (error) {
|
|
2298
|
+
logger.error("llm-pi: deepseek route 更新失败,保留此前注册");
|
|
2299
|
+
logger.error(error);
|
|
2300
|
+
}
|
|
2301
|
+
try {
|
|
2302
|
+
ensureDirectory();
|
|
2303
|
+
} catch (error) {
|
|
2304
|
+
logger.error("llm-pi: 更新被拒,保留此前的 configurable-provider 目录");
|
|
2305
|
+
logger.error(error);
|
|
2306
|
+
}
|
|
2307
|
+
const cfg = current();
|
|
2308
|
+
modelsDev.reconfigure(cfg.catalogUrl, cfg.catalogRefreshHours, cfg.catalogProxy ?? "");
|
|
2309
|
+
};
|
|
2310
|
+
ctx.events.on("loader/volatile-update", reconfigure);
|
|
2311
|
+
ctx.on("internal/config", function(_raw, next) {
|
|
2312
|
+
const candidate = next();
|
|
2313
|
+
if (this !== ctx.fiber) return candidate;
|
|
2314
|
+
assertServiceable(unwrapVolatile(Config(candidate)), deps);
|
|
2315
|
+
return candidate;
|
|
2315
2316
|
});
|
|
2316
2317
|
return {
|
|
2317
2318
|
currentConfig: () => current(),
|
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.34",
|
|
4
4
|
"description": "dsh-plus service+ui plugin: 基于 PiAiAdapter 的自定义 LLM 路由(全量 compat、模型继承、models.dev 目录兜底)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -32,41 +32,41 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"https-proxy-agent": "^9.1.0",
|
|
35
|
-
"@dsh-plus/shared": "0.1.
|
|
35
|
+
"@dsh-plus/shared": "0.1.23"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
39
|
-
"@deepseek-ai/dsh-anonymous-user-id": "^0.1.
|
|
40
|
-
"@deepseek-ai/dsh-credentials": "^0.1.
|
|
41
|
-
"@deepseek-ai/dsh-home-paths": "^0.1.
|
|
42
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.
|
|
43
|
-
"@deepseek-ai/dsh-launch-environment": "^0.1.
|
|
44
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
45
|
-
"@deepseek-ai/dsh-llm-deepseek": "^0.1.
|
|
46
|
-
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.
|
|
47
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
48
|
-
"@deepseek-ai/dsh-util-values": "^0.1.
|
|
49
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
38
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
39
|
+
"@deepseek-ai/dsh-anonymous-user-id": "^0.1.7-rc.1",
|
|
40
|
+
"@deepseek-ai/dsh-credentials": "^0.1.7-rc.1",
|
|
41
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.7-rc.1",
|
|
42
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.1",
|
|
43
|
+
"@deepseek-ai/dsh-launch-environment": "^0.1.7-rc.1",
|
|
44
|
+
"@deepseek-ai/dsh-llm": "^0.1.7-rc.1",
|
|
45
|
+
"@deepseek-ai/dsh-llm-deepseek": "^0.1.7-rc.1",
|
|
46
|
+
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.7-rc.1",
|
|
47
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-rc.1",
|
|
48
|
+
"@deepseek-ai/dsh-util-values": "^0.1.7-rc.1",
|
|
49
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
50
50
|
"@earendil-works/pi-ai": "^0.85.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@deepseek-ai/cordis": "4.0.
|
|
54
|
-
"@deepseek-ai/dsh-anonymous-user-id": "0.1.
|
|
55
|
-
"@deepseek-ai/dsh-atomic-write": "0.1.
|
|
56
|
-
"@deepseek-ai/dsh-attachment": "0.1.
|
|
57
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
58
|
-
"@deepseek-ai/dsh-home-paths": "0.1.
|
|
59
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.
|
|
60
|
-
"@deepseek-ai/dsh-launch-environment": "0.1.
|
|
61
|
-
"@deepseek-ai/dsh-llm": "0.1.
|
|
62
|
-
"@deepseek-ai/dsh-llm-deepseek": "0.1.
|
|
63
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.1.
|
|
64
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
65
|
-
"@deepseek-ai/dsh-timeout": "0.1.
|
|
66
|
-
"@deepseek-ai/dsh-typert-protocol": "0.1.
|
|
67
|
-
"@deepseek-ai/dsh-util-crypto": "0.1.
|
|
68
|
-
"@deepseek-ai/dsh-util-values": "0.1.
|
|
69
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
53
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
54
|
+
"@deepseek-ai/dsh-anonymous-user-id": "0.1.7-rc.1",
|
|
55
|
+
"@deepseek-ai/dsh-atomic-write": "0.1.7-rc.1",
|
|
56
|
+
"@deepseek-ai/dsh-attachment": "0.1.7-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-credentials": "0.1.7-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-home-paths": "0.1.7-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
|
|
60
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.7-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-llm-deepseek": "0.1.7-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.1.7-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-timeout": "0.1.7-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-util-crypto": "0.1.7-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-rc.1",
|
|
69
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
70
70
|
"@earendil-works/pi-ai": "0.85.1",
|
|
71
71
|
"@types/react": "~18.3.31",
|
|
72
72
|
"react": "^18.2.0"
|
package/src/client/client.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* 类型说明:浏览器半只用到 slots/locale/remote 的很窄一面,
|
|
9
9
|
* scope 与基础控件走 @dsh-plus/shared/client 套件(收编自本插件原实现)。
|
|
10
10
|
* 配置读写经 ctx.remote.settings 直连(0.1.2-alpha.1 起 connection.api.settings
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* 已移除;0.1.7 起 settingsScope 服务亦删除(configForms 取代)——插件卡片自始
|
|
12
|
+
* 直连 remote.settings,见 @dsh-plus/shared/client scope.ts);自定义端点仅剩「模型目录」
|
|
13
13
|
* (api.ts,含 kitSource/models-dev 运行期诊断)。
|
|
14
14
|
* 卡片 key/rowId 约定见 @dsh-plus/shared/client/config-slots。
|
|
15
15
|
* @module @dsh-plus/llm-pi/client
|
package/src/config.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import z from '@deepseek-ai/schemastery'
|
|
13
|
+
import type { VolatileFields } from '@dsh-plus/shared'
|
|
13
14
|
|
|
14
15
|
import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
|
|
15
16
|
|
|
@@ -325,21 +326,34 @@ const providerProfile = z.object({
|
|
|
325
326
|
.description('本 route 的模型目录;缺省且 provider 有 extends 时继承该源全部模型'),
|
|
326
327
|
})
|
|
327
328
|
|
|
328
|
-
|
|
329
|
-
|
|
329
|
+
// 0.1.7:根字段全 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
|
|
330
|
+
// loader 原位提交活动引用;输出面标注活动字段声明面(结构兼容 volatile 引用),
|
|
331
|
+
// 平面消费契约保持 LlmPiConfig 接口不变(服务侧经 unwrapVolatile 解包现取)。
|
|
332
|
+
/** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
|
|
333
|
+
export type LlmPiConfigFields = VolatileFields<LlmPiConfig>
|
|
334
|
+
|
|
335
|
+
export const Config: z<LlmPiConfigInput, LlmPiConfigFields> = z.object({
|
|
336
|
+
enabled: z.boolean().description('总开关(关闭则不注册任何 route)').default(true).volatile(),
|
|
330
337
|
catalogUrl: z
|
|
331
338
|
.string()
|
|
332
339
|
.description('models.dev 目录数据端点')
|
|
333
|
-
.default('https://models.dev/api.json')
|
|
340
|
+
.default('https://models.dev/api.json')
|
|
341
|
+
.volatile(),
|
|
334
342
|
catalogRefreshHours: z
|
|
335
343
|
.number()
|
|
336
344
|
.description('models.dev 自动拉取间隔小时数;0 = 不自动拉取(可手动拉取或读已有缓存)')
|
|
337
|
-
.default(0)
|
|
345
|
+
.default(0)
|
|
346
|
+
.volatile(),
|
|
338
347
|
catalogProxy: z
|
|
339
348
|
.string()
|
|
340
349
|
.description('拉取 models.dev 目录时的 HTTP 代理地址(如 http://127.0.0.1:7890);留空直连')
|
|
341
|
-
.default('')
|
|
342
|
-
|
|
350
|
+
.default('')
|
|
351
|
+
.volatile(),
|
|
352
|
+
providers: z
|
|
353
|
+
.dict(providerProfile)
|
|
354
|
+
.description('provider 路由表,键即 route 名')
|
|
355
|
+
.default({})
|
|
356
|
+
.volatile(),
|
|
343
357
|
})
|
|
344
358
|
|
|
345
359
|
/** 卡片提交的形状:完整配置对象(含 providers 全量),settings.replace 整段覆盖。 */
|
package/src/service.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 运行时主逻辑:注册/热更新/发现(逐点对齐官方 dsh-llm-pi-ai apply 的模式)。
|
|
3
3
|
*
|
|
4
|
-
* - profiles 回调按原始 config 对象 identity 备忘;配置变更经
|
|
5
|
-
*
|
|
4
|
+
* - profiles 回调按原始 config 对象 identity 备忘;配置变更经 0.1.7
|
|
5
|
+
* volatile 原位提交(loader/volatile-update 换代快照)传播,下一请求生效
|
|
6
|
+
* (adapter 快照按 profiles identity 失效);
|
|
6
7
|
* - route 集或注册时捕获的事实(displayName/retryPolicy)变化 → 原子的
|
|
7
8
|
* handle.replace 重注册;写入被校验拒绝时保留旧注册(官方同款护栏);
|
|
8
9
|
* - registerConfigurableProviders + registerModelDiscovery 让插件 route
|
|
@@ -16,10 +17,16 @@ import type { DirectoryRegistrationHandle } from '@deepseek-ai/dsh-llm'
|
|
|
16
17
|
import type { ResolvedPiAiProviderProfile } from '@deepseek-ai/dsh-llm-pi-ai'
|
|
17
18
|
import type {} from '@deepseek-ai/dsh-settings'
|
|
18
19
|
import { deepEqualJson } from '@deepseek-ai/dsh-util-values'
|
|
19
|
-
import { pluginDataPath } from '@dsh-plus/shared'
|
|
20
|
+
import { hasVolatileRefs, pluginDataPath, unwrapVolatile } from '@dsh-plus/shared'
|
|
20
21
|
|
|
21
22
|
import { ModelsDevSource } from './catalog/models-dev.ts'
|
|
22
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
Config,
|
|
25
|
+
type LlmPiConfig,
|
|
26
|
+
type LlmPiConfigFields,
|
|
27
|
+
type LlmPiConfigInput,
|
|
28
|
+
SETTINGS_NS,
|
|
29
|
+
} from './config.ts'
|
|
23
30
|
import { DeepseekRouteRegistrar } from './deepseek-routes.ts'
|
|
24
31
|
import { buildDirectoryEntries, commitDirectory, type DirectoryEntry } from './directory.ts'
|
|
25
32
|
import { discoverModels } from './discovery.ts'
|
|
@@ -72,25 +79,34 @@ function makeResolveApiKey(ctx: Context, kit: DshKit) {
|
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/** 启动插件运行时:解析套件、挂载注册/发现/settings 联动。 */
|
|
75
|
-
export async function startRuntime(
|
|
82
|
+
export async function startRuntime(
|
|
83
|
+
ctx: Context,
|
|
84
|
+
rawConfig: LlmPiConfig | LlmPiConfigFields,
|
|
85
|
+
): Promise<LlmPiRuntime> {
|
|
76
86
|
const logger = ctx.logger('llm-pi')
|
|
77
87
|
// cordis 行级 config 可能未经 schema 解析(insert 行无 config 键时为原始空对象),
|
|
78
|
-
//
|
|
79
|
-
|
|
88
|
+
// 在此统一规范化;0.1.7 loader 解析出的 volatile 活动字段形态直接透传
|
|
89
|
+
// (hasVolatileRefs 辨识——重复校验会把引用再包一层、破坏 loader 原位提交)。
|
|
90
|
+
const fields: LlmPiConfigFields = hasVolatileRefs(rawConfig)
|
|
91
|
+
? (rawConfig as LlmPiConfigFields)
|
|
92
|
+
: Config((rawConfig ?? {}) as LlmPiConfigInput)
|
|
80
93
|
const { kit, diagnostics } = await resolveDshKit()
|
|
81
94
|
for (const line of diagnostics) logger.warn(line)
|
|
82
95
|
logger.info(`运行时套件来源:${kit.source}`)
|
|
83
96
|
|
|
97
|
+
// 活动引用解包出平面快照:identity 仅在 volatile-update 重解包时换代
|
|
98
|
+
// (profiles 备忘依赖 raw === lastRaw;非 volatile 变更走 fiber reload 整树重建)。
|
|
99
|
+
let snapshot = unwrapVolatile(fields)
|
|
84
100
|
const modelsDev = new ModelsDevSource(
|
|
85
101
|
pluginDataPath('llm-pi', 'models-dev.json'),
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
snapshot.catalogUrl,
|
|
103
|
+
snapshot.catalogRefreshHours,
|
|
88
104
|
(message) => logger.warn(message),
|
|
89
|
-
|
|
105
|
+
snapshot.catalogProxy ?? '',
|
|
90
106
|
)
|
|
91
107
|
void modelsDev.ensureLoaded()
|
|
92
108
|
|
|
93
|
-
|
|
109
|
+
const current: () => LlmPiConfig = () => snapshot
|
|
94
110
|
let lastRaw: LlmPiConfig | undefined
|
|
95
111
|
let memoized: Map<string, ResolvedPiAiProviderProfile> | undefined
|
|
96
112
|
let memoizedDeepseek: Map<string, ResolvedDeepseekRoute> | undefined
|
|
@@ -285,37 +301,39 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
285
301
|
ensureDeepseek()
|
|
286
302
|
ensureDirectory()
|
|
287
303
|
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
304
|
+
// 0.1.7 替代 installSection(validate/setSource/onChange 三钩子):
|
|
305
|
+
// - 活动引用原位提交 → volatile-update 触发快照换代与重注册(原 onChange 体);
|
|
306
|
+
// - internal/config waterfall 承接写入校验(原 validate;官方 llm-pi-ai
|
|
307
|
+
// 同款):configEditor.edit 落盘前先跑 waterfall,handler throw 即拒绝整笔写入。
|
|
308
|
+
const reconfigure = (): void => {
|
|
309
|
+
snapshot = unwrapVolatile(fields)
|
|
310
|
+
try {
|
|
311
|
+
ensureRegistration()
|
|
312
|
+
} catch (error) {
|
|
313
|
+
logger.error('llm-pi: 更新被拒,保留此前注册的 route')
|
|
314
|
+
logger.error(error)
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
ensureDeepseek()
|
|
318
|
+
} catch (error) {
|
|
319
|
+
logger.error('llm-pi: deepseek route 更新失败,保留此前注册')
|
|
320
|
+
logger.error(error)
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
ensureDirectory()
|
|
324
|
+
} catch (error) {
|
|
325
|
+
logger.error('llm-pi: 更新被拒,保留此前的 configurable-provider 目录')
|
|
326
|
+
logger.error(error)
|
|
327
|
+
}
|
|
328
|
+
const cfg = current()
|
|
329
|
+
modelsDev.reconfigure(cfg.catalogUrl, cfg.catalogRefreshHours, cfg.catalogProxy ?? '')
|
|
330
|
+
}
|
|
331
|
+
ctx.events.on('loader/volatile-update', reconfigure)
|
|
332
|
+
ctx.on('internal/config', function (_raw: unknown, next: () => unknown) {
|
|
333
|
+
const candidate = next()
|
|
334
|
+
if (this !== ctx.fiber) return candidate
|
|
335
|
+
assertServiceable(unwrapVolatile(Config(candidate as LlmPiConfigInput)), deps)
|
|
336
|
+
return candidate
|
|
319
337
|
})
|
|
320
338
|
|
|
321
339
|
return {
|