@dsh-plus/llm-pi 0.2.0 → 0.2.1
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 +2 -2
- package/lib/index.js +52 -20
- package/package.json +2 -2
- package/src/client/client.ts +2 -2
- 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 +4 -4
- package/src/service.ts +6 -6
package/lib/client.js
CHANGED
|
@@ -184,7 +184,7 @@ window.__ModuleLoader__.load({
|
|
|
184
184
|
unsaved: "未保存",
|
|
185
185
|
loading: "加载中…",
|
|
186
186
|
unavailable: "配置服务不可用。",
|
|
187
|
-
readOnly: "当前部署无 settings provider
|
|
187
|
+
readOnly: "当前部署无 settings provider,配置为只读;请在 profile 的 cordis.patch.yml 行级配置。",
|
|
188
188
|
invalidNumber: "请输入正整数。",
|
|
189
189
|
collapse: "收起",
|
|
190
190
|
expand: "展开",
|
|
@@ -201,7 +201,7 @@ window.__ModuleLoader__.load({
|
|
|
201
201
|
unsaved: "Unsaved",
|
|
202
202
|
loading: "Loading…",
|
|
203
203
|
unavailable: "Settings service unavailable.",
|
|
204
|
-
readOnly: "No settings provider in this deployment; config is read-only.
|
|
204
|
+
readOnly: "No settings provider in this deployment; config is read-only. Configure it at the profile row in cordis.patch.yml.",
|
|
205
205
|
invalidNumber: "Enter a positive integer.",
|
|
206
206
|
collapse: "Collapse",
|
|
207
207
|
expand: "Expand",
|
|
@@ -408,7 +408,7 @@ ${extra}
|
|
|
408
408
|
//#region src/ns.ts
|
|
409
409
|
/**
|
|
410
410
|
* settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
|
|
411
|
-
* 服务端直接以字面量注册(
|
|
411
|
+
* 服务端直接以字面量注册(dsh-settings 对命名空间做编译期
|
|
412
412
|
* 语法校验,见 config.ts);浏览器半把它作为配置卡片的注册键
|
|
413
413
|
* (插件页配置卡片挂在 row/bundle config 槽位上,与命名空间解耦,
|
|
414
414
|
* 见 shared/config-slots),
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import z from "@deepseek-ai/schemastery";
|
|
|
2
2
|
import { VolatileFields } from "@dsh-plus/shared";
|
|
3
3
|
import { Context } from "@deepseek-ai/cordis";
|
|
4
4
|
//#region src/config.d.ts
|
|
5
|
-
/** settings
|
|
5
|
+
/** settings 命名空间(字面量即合法命名空间,编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
|
|
6
6
|
declare const SETTINGS_NS = "dsh-plus-llm-pi";
|
|
7
7
|
/** 线协议标识;合法集合运行期推导,故此处是开放字符串而非字面量联合。 */
|
|
8
8
|
type ProtocolId = string;
|
|
@@ -89,7 +89,7 @@ interface LlmPiConfig {
|
|
|
89
89
|
* (2 个根字段均有 schema 默认值),解析输出仍是完整的 {@link LlmPiConfig}。
|
|
90
90
|
*/
|
|
91
91
|
type LlmPiConfigInput = Partial<LlmPiConfig>;
|
|
92
|
-
/** 活动字段形态(
|
|
92
|
+
/** 活动字段形态(loader 解析产物:volatile 字段为活动引用)。 */
|
|
93
93
|
type LlmPiConfigFields = VolatileFields<LlmPiConfig>;
|
|
94
94
|
declare const Config: z<LlmPiConfigInput, LlmPiConfigFields>;
|
|
95
95
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { homedir } from "node:os";
|
|
|
24
24
|
//#region src/config.ts
|
|
25
25
|
/**
|
|
26
26
|
* 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
|
|
27
|
-
|
|
27
|
+
*(用户层=profile 行级覆盖层 cordis.patch.yml,热生效)共用同一
|
|
28
28
|
* schemastery schema。无密钥字段(apiKeyEnv 是凭据引用名而非密钥本身)。
|
|
29
29
|
*
|
|
30
30
|
* 与官方 llm-pi-ai 的差异:
|
|
@@ -35,10 +35,10 @@ import { homedir } from "node:os";
|
|
|
35
35
|
* 官方将来新增协议无需改本插件。
|
|
36
36
|
* @module llm-pi/config
|
|
37
37
|
*/
|
|
38
|
-
/** settings
|
|
38
|
+
/** settings 命名空间(字面量即合法命名空间,编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
|
|
39
39
|
const SETTINGS_NS = "dsh-plus-llm-pi";
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* 官方适配器不可用/推导失败时的协议兜底三元组。
|
|
42
42
|
* 运行期生效集合来自已安装官方包的 `supportedProtocols()`(见 resolve-dsh.ts),
|
|
43
43
|
* 本常量只用于 schema 描述、浏览器半首帧兜底与推导失败回退。
|
|
44
44
|
*/
|
|
@@ -68,8 +68,6 @@ const DEEPSEEK_REASONING_EFFORTS = [
|
|
|
68
68
|
"high",
|
|
69
69
|
"max"
|
|
70
70
|
];
|
|
71
|
-
/** 旧版 imageDetail 枚举(0.1.2-alpha.1 已移除该字段;仅用于 schema 透传陷阱的取值集)。 */
|
|
72
|
-
const DEEPSEEK_IMAGE_DETAILS = ["auto", "low"];
|
|
73
71
|
/** dsh-timeout 的定时器上限(与官方 MAX_TIMER_DELAY_MS 对齐)。 */
|
|
74
72
|
const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
75
73
|
/**
|
|
@@ -103,8 +101,7 @@ const modelEntry = z.object({
|
|
|
103
101
|
reasoningEfforts: z.union([z.const(false), reasoningEfforts]).description("可选 reasoning 档位:false=非推理模型;dict=档位→线值映射;缺省继承内置目录能力"),
|
|
104
102
|
compat: compatDict.description("模型级 compat(字段级合并,压过 route 级与继承值)"),
|
|
105
103
|
imagePixelBudget: z.number().step(1).min(1).description("(仅 adapter: deepseek)单张请求图片的总像素预算;缺省继承官方目录"),
|
|
106
|
-
imageMaxBytes: z.number().step(1).min(1).description("(仅 adapter: deepseek)单张请求图片的编码字节上限;缺省继承官方目录")
|
|
107
|
-
imageDetail: z.union(DEEPSEEK_IMAGE_DETAILS).description("(已移除)图片细节档位;0.1.2-alpha.1 起请改用 imagePixelBudget/imageMaxBytes")
|
|
104
|
+
imageMaxBytes: z.number().step(1).min(1).description("(仅 adapter: deepseek)单张请求图片的编码字节上限;缺省继承官方目录")
|
|
108
105
|
});
|
|
109
106
|
const providerProfile = z.object({
|
|
110
107
|
adapter: z.union(ADAPTER_KINDS).description("路由适配器:pi=PiAiAdapter(默认,协议集合随已装官方适配器推导);deepseek=官方 DeepSeekAdapter(图片走 Files API 文件通道,失败自动降级 base64)").default("pi"),
|
|
@@ -134,7 +131,7 @@ const providerProfile = z.object({
|
|
|
134
131
|
timeoutMs: z.natural().description("HTTP/provider 超时毫秒"),
|
|
135
132
|
websocketConnectTimeoutMs: z.natural().description("WebSocket 连接超时毫秒"),
|
|
136
133
|
streamIdleTimeoutMs: z.number().min(Number.MIN_VALUE).max(MAX_TIMER_DELAY_MS).description("单支流式读取的最大空闲间隔毫秒"),
|
|
137
|
-
maxRequestImageBytes: z.natural().description("单请求 base64 图片载荷上限字节;缺省 20MiB
|
|
134
|
+
maxRequestImageBytes: z.natural().description("单请求 base64 图片载荷上限字节;缺省 20MiB(官方必需字段,旧配置免改)"),
|
|
138
135
|
requestImagePixelBudget: z.number().step(1).min(1).description("单请求每个确定性内联图片版本的像素总预算;缺省 2048x2048(官方同值)"),
|
|
139
136
|
requestImageMaxBytes: z.number().step(1).min(1).description("内联图片版本编码字节目标;缺省 1MiB(官方同值)"),
|
|
140
137
|
retryPolicy: z.any().description("provider 重试策略(dsh-llm RetryPolicy 形状,构建期校验)"),
|
|
@@ -517,7 +514,7 @@ function browseModels(kit, query = {}) {
|
|
|
517
514
|
* 3. **模型条目字段集**:Config schema 的 `providers.*.models` 键集——继承时按此
|
|
518
515
|
* 透传 pi-ai 目录的同名字段,官方将来新增模型级字段即自动跟随。
|
|
519
516
|
*
|
|
520
|
-
*
|
|
517
|
+
* 历史教训(详见 docs/repo/事故记录.md):compat 门控曾是官方的手抄镜像,官方扩容
|
|
521
518
|
* offer 字段而旧表漏收,导致官方可配字段被本插件**误拒**(静默功能缺失、无报错)。
|
|
522
519
|
* 现改为现场推导,`tests/official-surface.test.ts` 直读官方副本守门推导正确性。
|
|
523
520
|
*
|
|
@@ -652,10 +649,18 @@ const FALLBACK_MODEL_ENTRY_FIELDS = [
|
|
|
652
649
|
function fallbackProtocols() {
|
|
653
650
|
return [...FALLBACK_PROTOCOLS];
|
|
654
651
|
}
|
|
655
|
-
/**
|
|
652
|
+
/**
|
|
653
|
+
* 官方门控表不可解析时的兜底快照(0.2.1-alpha.1 现场推导值)。
|
|
654
|
+
*
|
|
655
|
+
* 只在官方改打包形态、`COMPAT_GATES` 解析失败时启用(此时未知键放行、只拦快照里
|
|
656
|
+
* 明确 withhold 的键,见 compat.ts 的「语义分两档」)。快照过期会重新引入
|
|
657
|
+
* 「误拒官方新字段」那类静默故障,故由 tests/official-surface.test.ts 的
|
|
658
|
+
* 「兜底快照不得过期」用例与现场推导逐协议比对守门。
|
|
659
|
+
*/
|
|
656
660
|
const FALLBACK_TABLE = {
|
|
657
661
|
source: "fallback",
|
|
658
662
|
gates: {
|
|
663
|
+
"mistral-conversations": { supportsMidConvoSystemMessages: "withhold" },
|
|
659
664
|
"openai-completions": {
|
|
660
665
|
supportsStore: "offer",
|
|
661
666
|
supportsDeveloperRole: "offer",
|
|
@@ -681,7 +686,8 @@ const FALLBACK_TABLE = {
|
|
|
681
686
|
zaiToolStream: "withhold",
|
|
682
687
|
supportsOpenAIGrammarTools: "withhold",
|
|
683
688
|
sendSessionAffinityHeaders: "withhold",
|
|
684
|
-
|
|
689
|
+
supportsMidConvoSystemMessages: "withhold",
|
|
690
|
+
supportsMidConvoToolAdditions: "withhold",
|
|
685
691
|
sessionAffinityFormat: "withhold"
|
|
686
692
|
},
|
|
687
693
|
"openai-responses": {
|
|
@@ -693,7 +699,32 @@ const FALLBACK_TABLE = {
|
|
|
693
699
|
supportsOpenAIGrammarTools: "withhold",
|
|
694
700
|
supportsAdditionalTools: "withhold",
|
|
695
701
|
supportsToolSearch: "withhold",
|
|
696
|
-
supportsExplicitPromptCacheMode: "withhold"
|
|
702
|
+
supportsExplicitPromptCacheMode: "withhold",
|
|
703
|
+
supportsMidConvoSystemMessages: "withhold"
|
|
704
|
+
},
|
|
705
|
+
"azure-openai-responses": {
|
|
706
|
+
supportsDeveloperRole: "offer",
|
|
707
|
+
supportsMaxOutputTokens: "offer",
|
|
708
|
+
supportsStrictMode: "offer",
|
|
709
|
+
supportsLongCacheRetention: "offer",
|
|
710
|
+
sessionAffinityFormat: "withhold",
|
|
711
|
+
supportsOpenAIGrammarTools: "withhold",
|
|
712
|
+
supportsAdditionalTools: "withhold",
|
|
713
|
+
supportsToolSearch: "withhold",
|
|
714
|
+
supportsExplicitPromptCacheMode: "withhold",
|
|
715
|
+
supportsMidConvoSystemMessages: "withhold"
|
|
716
|
+
},
|
|
717
|
+
"openai-codex-responses": {
|
|
718
|
+
supportsDeveloperRole: "offer",
|
|
719
|
+
supportsMaxOutputTokens: "offer",
|
|
720
|
+
supportsStrictMode: "offer",
|
|
721
|
+
supportsLongCacheRetention: "offer",
|
|
722
|
+
sessionAffinityFormat: "withhold",
|
|
723
|
+
supportsOpenAIGrammarTools: "withhold",
|
|
724
|
+
supportsAdditionalTools: "withhold",
|
|
725
|
+
supportsToolSearch: "withhold",
|
|
726
|
+
supportsExplicitPromptCacheMode: "withhold",
|
|
727
|
+
supportsMidConvoSystemMessages: "withhold"
|
|
697
728
|
},
|
|
698
729
|
"anthropic-messages": {
|
|
699
730
|
supportsEagerToolInputStreaming: "offer",
|
|
@@ -704,10 +735,13 @@ const FALLBACK_TABLE = {
|
|
|
704
735
|
allowEmptySignature: "offer",
|
|
705
736
|
supportsStrictTools: "offer",
|
|
706
737
|
sendSessionAffinityHeaders: "withhold",
|
|
707
|
-
|
|
738
|
+
sessionAffinityFormat: "withhold",
|
|
739
|
+
supportsMidConvoSystemMessages: "withhold",
|
|
740
|
+
supportsMidConvoToolChanges: "withhold",
|
|
708
741
|
supportsMidConvoEffort: "withhold",
|
|
709
742
|
allowedFallbackModels: "withhold"
|
|
710
|
-
}
|
|
743
|
+
},
|
|
744
|
+
"bedrock-converse-stream": { supportsStrictMode: "offer" }
|
|
711
745
|
},
|
|
712
746
|
specs: {
|
|
713
747
|
supportsStore: "boolean",
|
|
@@ -821,7 +855,7 @@ function checkValue(field, spec, value, where) {
|
|
|
821
855
|
}
|
|
822
856
|
/**
|
|
823
857
|
* 校验一份 compat 字典对指定协议合法(对齐官方门控语义 + schema 值约束):
|
|
824
|
-
* - 未知键/withhold
|
|
858
|
+
* - 未知键/withhold 字段拒绝(与官方写时拒绝同款);
|
|
825
859
|
* - 值类型/枚举按官方 schema 校验;
|
|
826
860
|
* - 无值键(null/undefined)拒绝(官方 assertOfferedCompatFields 同款)。
|
|
827
861
|
*/
|
|
@@ -1527,7 +1561,6 @@ function declaredModalities(configured) {
|
|
|
1527
1561
|
/** 物化单个模型目录条目:官方 base 在下,条目显式字段逐字段覆盖。 */
|
|
1528
1562
|
function materializeModel$1(route, entry, deps) {
|
|
1529
1563
|
if (entry.reasoningEfforts !== void 0 || nonEmptyDict(entry.compat)) invalid$1(route, `model "${entry.id}" 的 reasoningEfforts/compat 仅 adapter: pi 可用`);
|
|
1530
|
-
if (entry.imageDetail !== void 0) invalid$1(route, `model "${entry.id}" 的 imageDetail 已随 0.1.2-alpha.1 移除;请改用 imagePixelBudget/imageMaxBytes(低细节可配更小的 imagePixelBudget)并删除 imageDetail`);
|
|
1531
1564
|
const base = resolveOfficialBase(route, entry, deps);
|
|
1532
1565
|
const input = declaredModalities(entry.input) ?? base.input;
|
|
1533
1566
|
const imageFields = {
|
|
@@ -1588,7 +1621,7 @@ function rawAdapterConfig(profile, baseURL, models) {
|
|
|
1588
1621
|
/** 物化单个 deepseek route;lenient 下失败告警并返回 null(调用方跳过注册)。 */
|
|
1589
1622
|
function buildRoute(route, profile, deps) {
|
|
1590
1623
|
try {
|
|
1591
|
-
if (deps.kit.deepseek === void 0) invalid$1(route, "使用 adapter: deepseek,但当前运行时套件不含 dsh-llm-deepseek
|
|
1624
|
+
if (deps.kit.deepseek === void 0) invalid$1(route, "使用 adapter: deepseek,但当前运行时套件不含 dsh-llm-deepseek(本仓基线的必需套件)");
|
|
1592
1625
|
rejectPiOnlyFields(route, profile);
|
|
1593
1626
|
if (profile.extends !== void 0 && profile.extends !== "deepseek") invalid$1(route, `的 extends ${JSON.stringify(profile.extends)} 非法:adapter: deepseek 仅支持 "deepseek"(官方目录)`);
|
|
1594
1627
|
if (profile.apiKeyEnv === void 0 || profile.apiKeyEnv.length === 0) invalid$1(route, "需要 apiKeyEnv:DeepSeekAdapter 无环境自发现,凭据引用必须显式配置");
|
|
@@ -1625,7 +1658,7 @@ function buildDeepseekRoutes(providers, deps) {
|
|
|
1625
1658
|
//#endregion
|
|
1626
1659
|
//#region src/profiles.ts
|
|
1627
1660
|
/**
|
|
1628
|
-
* profile
|
|
1661
|
+
* profile 构建器:插件 Config → 官方 schema 形状的
|
|
1629
1662
|
* 「前置数据源归一化」→ 官方 resolveProfiles(或插件等价兜底)→
|
|
1630
1663
|
* ResolvedPiAiProviderProfile。
|
|
1631
1664
|
*
|
|
@@ -1740,7 +1773,6 @@ function reasoningEffortsFromBase(provider, entry, base) {
|
|
|
1740
1773
|
* 返回 null(调用方跳过该模型)。
|
|
1741
1774
|
*/
|
|
1742
1775
|
function materializeModel(route, profile, entry, base, routeApi, defaultInput, deps) {
|
|
1743
|
-
if (entry.imageDetail !== void 0) invalid(route, `model "${entry.id}" 的 imageDetail 已随 0.1.2-alpha.1 移除;请改用 imagePixelBudget/imageMaxBytes(仅 adapter: deepseek)并删除 imageDetail`);
|
|
1744
1776
|
const api = profile.api ?? base.api ?? routeApi;
|
|
1745
1777
|
if (api === void 0) {
|
|
1746
1778
|
if (deps.lenient) {
|
|
@@ -2558,7 +2590,7 @@ function authContextFrom(ctx) {
|
|
|
2558
2590
|
* 模型条目字段集同样现场推导(official-surface.ts)。生效版本只用于状态行显示,
|
|
2559
2591
|
* 超出已验证区间也只是提示(kit-meta.ts),永不阻断。
|
|
2560
2592
|
*
|
|
2561
|
-
*
|
|
2593
|
+
* 仅 src 子路径导出、包根不导出的两块面:
|
|
2562
2594
|
* - resolveProfiles(config.ts):官方解析链,dev 布局树可经 src 子路径复用;
|
|
2563
2595
|
* - credentialStoreFrom/authContextFrom(auth.ts):PiAiAdapter 必需 auth 注入。
|
|
2564
2596
|
* npm 发布形态(lib/index.js 单 bundle + 无 src/)两者都拿不到:dsh 树优先
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/llm-pi",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "dsh-plus service+ui plugin: 基于 PiAiAdapter 的自定义 LLM 路由(全量 compat、模型继承、pi-ai 内置目录;协议/compat/字段集现场推导,自动跟随已装 dsh)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"yaml": "^2.9.0",
|
|
35
|
-
"@dsh-plus/shared": "0.1.
|
|
35
|
+
"@dsh-plus/shared": "0.1.36"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.5-alpha.1",
|
package/src/client/client.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 类型说明:浏览器半只用到 slots/locale/remote 的很窄一面,
|
|
9
9
|
* scope 与基础控件走 @dsh-plus/shared/client 套件(收编自本插件原实现)。
|
|
10
|
-
* 配置读写经 ctx.remote.settings 直连(
|
|
11
|
-
* 已移除;
|
|
10
|
+
* 配置读写经 ctx.remote.settings 直连(connection.api.settings
|
|
11
|
+
* 已移除; settingsScope 服务亦删除(configForms 取代)——插件卡片自始
|
|
12
12
|
* 直连 remote.settings,见 @dsh-plus/shared/client scope.ts);自定义端点只剩
|
|
13
13
|
* `/catalog`(api.ts:运行期套件事实 + pi-ai 内置目录搜索,浏览器半读不到 pi-ai 包)。
|
|
14
14
|
* 卡片 key/rowId 约定见 @dsh-plus/shared/client/config-slots。
|
package/src/compat.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* assertOfferedCompatFields:"写了但没生效"的表面状态不允许)。
|
|
11
11
|
* - **fallback(推导失败,用最后已知快照)**:只拒绝快照中明确 withhold 的键,
|
|
12
12
|
* **未知键放行**交给官方适配器自身校验——表落后时"误拒官方新字段"比"多放一个键"
|
|
13
|
-
*
|
|
13
|
+
* 严重得多(教训:静默功能缺失、无任何报错)。
|
|
14
14
|
*
|
|
15
15
|
* pi-ai 侧消费语义:getCompat 逐字段 `??` 覆盖 detectCompat 的 baseURL/名称猜测;
|
|
16
16
|
* undefined 视为未设置(无法显式清空检测值)。
|
|
@@ -122,7 +122,7 @@ function checkValue(field: string, spec: CompatValue, value: unknown, where: str
|
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
124
|
* 校验一份 compat 字典对指定协议合法(对齐官方门控语义 + schema 值约束):
|
|
125
|
-
* - 未知键/withhold
|
|
125
|
+
* - 未知键/withhold 字段拒绝(与官方写时拒绝同款);
|
|
126
126
|
* - 值类型/枚举按官方 schema 校验;
|
|
127
127
|
* - 无值键(null/undefined)拒绝(官方 assertOfferedCompatFields 同款)。
|
|
128
128
|
*/
|
package/src/config-api.ts
CHANGED
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
|
|
3
|
-
|
|
3
|
+
*(用户层=profile 行级覆盖层 cordis.patch.yml,热生效)共用同一
|
|
4
4
|
* schemastery schema。无密钥字段(apiKeyEnv 是凭据引用名而非密钥本身)。
|
|
5
5
|
*
|
|
6
6
|
* 与官方 llm-pi-ai 的差异:
|
|
@@ -17,11 +17,11 @@ import type { VolatileFields } from '@dsh-plus/shared'
|
|
|
17
17
|
|
|
18
18
|
import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
|
|
19
19
|
|
|
20
|
-
/** settings
|
|
20
|
+
/** settings 命名空间(字面量即合法命名空间,编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
|
|
21
21
|
export const SETTINGS_NS = NS_LITERAL
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* 官方适配器不可用/推导失败时的协议兜底三元组。
|
|
25
25
|
* 运行期生效集合来自已安装官方包的 `supportedProtocols()`(见 resolve-dsh.ts),
|
|
26
26
|
* 本常量只用于 schema 描述、浏览器半首帧兜底与推导失败回退。
|
|
27
27
|
*/
|
|
@@ -45,9 +45,6 @@ export type AdapterKind = (typeof ADAPTER_KINDS)[number]
|
|
|
45
45
|
/** deepseek 路由的思考/推理档位(线协议枚举,对齐官方 llm-deepseek)。 */
|
|
46
46
|
export const DEEPSEEK_THINKING = ['enabled', 'disabled'] as const
|
|
47
47
|
export const DEEPSEEK_REASONING_EFFORTS = ['off', 'low', 'high', 'max'] as const
|
|
48
|
-
/** 旧版 imageDetail 枚举(0.1.2-alpha.1 已移除该字段;仅用于 schema 透传陷阱的取值集)。 */
|
|
49
|
-
export const DEEPSEEK_IMAGE_DETAILS = ['auto', 'low'] as const
|
|
50
|
-
|
|
51
48
|
export const DEFAULT_CONTEXT_WINDOW = 262144
|
|
52
49
|
export const DEFAULT_MAX_TOKENS = 32768
|
|
53
50
|
export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000
|
|
@@ -201,12 +198,6 @@ const modelEntry = z.object({
|
|
|
201
198
|
.step(1)
|
|
202
199
|
.min(1)
|
|
203
200
|
.description('(仅 adapter: deepseek)单张请求图片的编码字节上限;缺省继承官方目录'),
|
|
204
|
-
// 0.1.2-alpha.1 移除字段的 schema 透传陷阱:官方已删除 imageDetail(改用
|
|
205
|
-
// imagePixelBudget/imageMaxBytes),此处保留取值 union 让旧配置通过 schema、
|
|
206
|
-
// 在写时校验(assertServiceable)以中文迁移提示明确拒绝(见 profiles-deepseek.ts)。
|
|
207
|
-
imageDetail: z
|
|
208
|
-
.union(DEEPSEEK_IMAGE_DETAILS)
|
|
209
|
-
.description('(已移除)图片细节档位;0.1.2-alpha.1 起请改用 imagePixelBudget/imageMaxBytes'),
|
|
210
201
|
})
|
|
211
202
|
|
|
212
203
|
const providerProfile = z.object({
|
|
@@ -254,9 +245,7 @@ const providerProfile = z.object({
|
|
|
254
245
|
.description('单支流式读取的最大空闲间隔毫秒'),
|
|
255
246
|
maxRequestImageBytes: z
|
|
256
247
|
.natural()
|
|
257
|
-
.description(
|
|
258
|
-
'单请求 base64 图片载荷上限字节;缺省 20MiB(0.1.2-alpha.1 起官方必需字段,旧配置免改)',
|
|
259
|
-
),
|
|
248
|
+
.description('单请求 base64 图片载荷上限字节;缺省 20MiB(官方必需字段,旧配置免改)'),
|
|
260
249
|
requestImagePixelBudget: z
|
|
261
250
|
.number()
|
|
262
251
|
.step(1)
|
|
@@ -333,10 +322,10 @@ const providerProfile = z.object({
|
|
|
333
322
|
.description('本 route 的模型目录;缺省且 provider 有 extends 时继承该源全部模型'),
|
|
334
323
|
})
|
|
335
324
|
|
|
336
|
-
//
|
|
325
|
+
// 根字段全 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
|
|
337
326
|
// loader 原位提交活动引用;输出面标注活动字段声明面(结构兼容 volatile 引用),
|
|
338
327
|
// 平面消费契约保持 LlmPiConfig 接口不变(服务侧经 unwrapVolatile 解包现取)。
|
|
339
|
-
/** 活动字段形态(
|
|
328
|
+
/** 活动字段形态(loader 解析产物:volatile 字段为活动引用)。 */
|
|
340
329
|
export type LlmPiConfigFields = VolatileFields<LlmPiConfig>
|
|
341
330
|
|
|
342
331
|
export const Config: z<LlmPiConfigInput, LlmPiConfigFields> = z.object({
|
package/src/deepseek-routes.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* 请求生效);retryPolicy 是注册期捕获事实,变化时 handle.replace 原地重注册
|
|
12
12
|
* (官方同款模式);route 移除即 dispose 释放路由名。
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* DeepSeekAdapterOptions 必需 prepareExtensions(官方接线
|
|
15
15
|
* ctx.get('deepseekLlmApiExtensions'),缺省空实现)与可选 resolveImageAccess。
|
|
16
16
|
* @module llm-pi/deepseek-routes
|
|
17
17
|
*/
|
|
@@ -158,7 +158,7 @@ export class DeepseekRouteRegistrar {
|
|
|
158
158
|
)?.processPathFromHostPath(hostPath),
|
|
159
159
|
ref,
|
|
160
160
|
) as never,
|
|
161
|
-
//
|
|
161
|
+
// 必需项:官方扩展注册表(deepseekLlmApiExtensions)缺席时
|
|
162
162
|
// 给空实现(官方接线范式,llm-deepseek/src/index.ts:404-470)。
|
|
163
163
|
prepareExtensions: (request) => {
|
|
164
164
|
const extensions = ctx.get('deepseekLlmApiExtensions') as
|
package/src/inherit.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 数据源只有一个:**pi-ai 内置目录**(随已装 dsh 的 pi-ai 版本自动跟随,
|
|
5
5
|
* 含官方校正的 compat/thinkingLevelMap/模态)。未命中即「手写条目」,
|
|
6
|
-
* 必填字段由 route 级配置或兜底给出。models.dev
|
|
6
|
+
* 必填字段由 route 级配置或兜底给出。models.dev 兜底源已移除——
|
|
7
7
|
* 版本漂移靠「现场推导 + 状态行显示生效版本」处理,不引入第二份可能过期的目录。
|
|
8
8
|
*
|
|
9
9
|
* 引用语法:`"provider/model"` 显式引用;裸 `"model"` 随 route 级 extends 源;
|
package/src/ns.ts
CHANGED
package/src/official-surface.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 3. **模型条目字段集**:Config schema 的 `providers.*.models` 键集——继承时按此
|
|
11
11
|
* 透传 pi-ai 目录的同名字段,官方将来新增模型级字段即自动跟随。
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* 历史教训(详见 docs/repo/事故记录.md):compat 门控曾是官方的手抄镜像,官方扩容
|
|
14
14
|
* offer 字段而旧表漏收,导致官方可配字段被本插件**误拒**(静默功能缺失、无报错)。
|
|
15
15
|
* 现改为现场推导,`tests/official-surface.test.ts` 直读官方副本守门推导正确性。
|
|
16
16
|
*
|
|
@@ -205,10 +205,20 @@ export function fallbackProtocols(): string[] {
|
|
|
205
205
|
return [...FALLBACK_PROTOCOLS]
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
/**
|
|
208
|
+
/**
|
|
209
|
+
* 官方门控表不可解析时的兜底快照(0.2.1-alpha.1 现场推导值)。
|
|
210
|
+
*
|
|
211
|
+
* 只在官方改打包形态、`COMPAT_GATES` 解析失败时启用(此时未知键放行、只拦快照里
|
|
212
|
+
* 明确 withhold 的键,见 compat.ts 的「语义分两档」)。快照过期会重新引入
|
|
213
|
+
* 「误拒官方新字段」那类静默故障,故由 tests/official-surface.test.ts 的
|
|
214
|
+
* 「兜底快照不得过期」用例与现场推导逐协议比对守门。
|
|
215
|
+
*/
|
|
209
216
|
export const FALLBACK_TABLE: CompatTable = {
|
|
210
217
|
source: 'fallback',
|
|
211
218
|
gates: {
|
|
219
|
+
'mistral-conversations': {
|
|
220
|
+
supportsMidConvoSystemMessages: 'withhold',
|
|
221
|
+
},
|
|
212
222
|
'openai-completions': {
|
|
213
223
|
supportsStore: 'offer',
|
|
214
224
|
supportsDeveloperRole: 'offer',
|
|
@@ -234,7 +244,8 @@ export const FALLBACK_TABLE: CompatTable = {
|
|
|
234
244
|
zaiToolStream: 'withhold',
|
|
235
245
|
supportsOpenAIGrammarTools: 'withhold',
|
|
236
246
|
sendSessionAffinityHeaders: 'withhold',
|
|
237
|
-
|
|
247
|
+
supportsMidConvoSystemMessages: 'withhold',
|
|
248
|
+
supportsMidConvoToolAdditions: 'withhold',
|
|
238
249
|
sessionAffinityFormat: 'withhold',
|
|
239
250
|
},
|
|
240
251
|
'openai-responses': {
|
|
@@ -247,6 +258,31 @@ export const FALLBACK_TABLE: CompatTable = {
|
|
|
247
258
|
supportsAdditionalTools: 'withhold',
|
|
248
259
|
supportsToolSearch: 'withhold',
|
|
249
260
|
supportsExplicitPromptCacheMode: 'withhold',
|
|
261
|
+
supportsMidConvoSystemMessages: 'withhold',
|
|
262
|
+
},
|
|
263
|
+
'azure-openai-responses': {
|
|
264
|
+
supportsDeveloperRole: 'offer',
|
|
265
|
+
supportsMaxOutputTokens: 'offer',
|
|
266
|
+
supportsStrictMode: 'offer',
|
|
267
|
+
supportsLongCacheRetention: 'offer',
|
|
268
|
+
sessionAffinityFormat: 'withhold',
|
|
269
|
+
supportsOpenAIGrammarTools: 'withhold',
|
|
270
|
+
supportsAdditionalTools: 'withhold',
|
|
271
|
+
supportsToolSearch: 'withhold',
|
|
272
|
+
supportsExplicitPromptCacheMode: 'withhold',
|
|
273
|
+
supportsMidConvoSystemMessages: 'withhold',
|
|
274
|
+
},
|
|
275
|
+
'openai-codex-responses': {
|
|
276
|
+
supportsDeveloperRole: 'offer',
|
|
277
|
+
supportsMaxOutputTokens: 'offer',
|
|
278
|
+
supportsStrictMode: 'offer',
|
|
279
|
+
supportsLongCacheRetention: 'offer',
|
|
280
|
+
sessionAffinityFormat: 'withhold',
|
|
281
|
+
supportsOpenAIGrammarTools: 'withhold',
|
|
282
|
+
supportsAdditionalTools: 'withhold',
|
|
283
|
+
supportsToolSearch: 'withhold',
|
|
284
|
+
supportsExplicitPromptCacheMode: 'withhold',
|
|
285
|
+
supportsMidConvoSystemMessages: 'withhold',
|
|
250
286
|
},
|
|
251
287
|
'anthropic-messages': {
|
|
252
288
|
supportsEagerToolInputStreaming: 'offer',
|
|
@@ -257,10 +293,15 @@ export const FALLBACK_TABLE: CompatTable = {
|
|
|
257
293
|
allowEmptySignature: 'offer',
|
|
258
294
|
supportsStrictTools: 'offer',
|
|
259
295
|
sendSessionAffinityHeaders: 'withhold',
|
|
260
|
-
|
|
296
|
+
sessionAffinityFormat: 'withhold',
|
|
297
|
+
supportsMidConvoSystemMessages: 'withhold',
|
|
298
|
+
supportsMidConvoToolChanges: 'withhold',
|
|
261
299
|
supportsMidConvoEffort: 'withhold',
|
|
262
300
|
allowedFallbackModels: 'withhold',
|
|
263
301
|
},
|
|
302
|
+
'bedrock-converse-stream': {
|
|
303
|
+
supportsStrictMode: 'offer',
|
|
304
|
+
},
|
|
264
305
|
},
|
|
265
306
|
specs: {
|
|
266
307
|
supportsStore: 'boolean',
|
package/src/profiles-deepseek.ts
CHANGED
|
@@ -130,16 +130,6 @@ function materializeModel(
|
|
|
130
130
|
if (entry.reasoningEfforts !== undefined || nonEmptyDict(entry.compat)) {
|
|
131
131
|
invalid(route, `model "${entry.id}" 的 reasoningEfforts/compat 仅 adapter: pi 可用`)
|
|
132
132
|
}
|
|
133
|
-
const legacyImageDetail = (entry as ModelEntryConfig & { imageDetail?: unknown }).imageDetail
|
|
134
|
-
if (legacyImageDetail !== undefined) {
|
|
135
|
-
// 0.1.2-alpha.1 移除字段:写时明确拒绝并给出迁移提示(官方 llm-deepseek
|
|
136
|
-
// 对含 imageDetail 的目录模型同样直接抛错,index.ts:212-215)。
|
|
137
|
-
invalid(
|
|
138
|
-
route,
|
|
139
|
-
`model "${entry.id}" 的 imageDetail 已随 0.1.2-alpha.1 移除;` +
|
|
140
|
-
'请改用 imagePixelBudget/imageMaxBytes(低细节可配更小的 imagePixelBudget)并删除 imageDetail',
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
133
|
const base = resolveOfficialBase(route, entry, deps)
|
|
144
134
|
const input = declaredModalities(entry.input) ?? base.input
|
|
145
135
|
const imageFields = {
|
|
@@ -233,7 +223,7 @@ function buildRoute(
|
|
|
233
223
|
if (deps.kit.deepseek === undefined) {
|
|
234
224
|
invalid(
|
|
235
225
|
route,
|
|
236
|
-
'使用 adapter: deepseek,但当前运行时套件不含 dsh-llm-deepseek
|
|
226
|
+
'使用 adapter: deepseek,但当前运行时套件不含 dsh-llm-deepseek(本仓基线的必需套件)',
|
|
237
227
|
)
|
|
238
228
|
}
|
|
239
229
|
rejectPiOnlyFields(route, profile)
|
package/src/profiles.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* profile
|
|
2
|
+
* profile 构建器:插件 Config → 官方 schema 形状的
|
|
3
3
|
* 「前置数据源归一化」→ 官方 resolveProfiles(或插件等价兜底)→
|
|
4
4
|
* ResolvedPiAiProviderProfile。
|
|
5
5
|
*
|
|
@@ -190,14 +190,6 @@ function materializeModel(
|
|
|
190
190
|
defaultInput: ('text' | 'image')[],
|
|
191
191
|
deps: BuildDeps,
|
|
192
192
|
): MaterializedEntry | null {
|
|
193
|
-
const legacyImageDetail = (entry as ModelEntryConfig & { imageDetail?: unknown }).imageDetail
|
|
194
|
-
if (legacyImageDetail !== undefined) {
|
|
195
|
-
invalid(
|
|
196
|
-
route,
|
|
197
|
-
`model "${entry.id}" 的 imageDetail 已随 0.1.2-alpha.1 移除;` +
|
|
198
|
-
'请改用 imagePixelBudget/imageMaxBytes(仅 adapter: deepseek)并删除 imageDetail',
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
193
|
const api = profile.api ?? base.api ?? routeApi
|
|
202
194
|
if (api === undefined) {
|
|
203
195
|
if (deps.lenient) {
|
|
@@ -606,7 +598,7 @@ export function resolveProfilesFallback(
|
|
|
606
598
|
// 模型物化:全显式条目 + route 级 api/baseURL(官方 resolveRouteModels 镜像,
|
|
607
599
|
// 无目录继承——归一化已把继承值落定在条目里)
|
|
608
600
|
const configuredMaxTokens = new Map<string, number>()
|
|
609
|
-
// 官方
|
|
601
|
+
// 官方 llm-pi-ai adapter 读取 profile.modelErrors/catalogError:
|
|
610
602
|
// 本 fallback 对条目严格校验、坏条目整体拒绝(不产生"条目级不可服务"的
|
|
611
603
|
// deferred 诊断),故补空表保持与官方 profile 形状一致——缺失会让
|
|
612
604
|
// PiAiAdapter.modelOf 读 modelErrors.get 时以 TypeError 打崩。
|
package/src/resolve-dsh.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* 模型条目字段集同样现场推导(official-surface.ts)。生效版本只用于状态行显示,
|
|
20
20
|
* 超出已验证区间也只是提示(kit-meta.ts),永不阻断。
|
|
21
21
|
*
|
|
22
|
-
*
|
|
22
|
+
* 仅 src 子路径导出、包根不导出的两块面:
|
|
23
23
|
* - resolveProfiles(config.ts):官方解析链,dev 布局树可经 src 子路径复用;
|
|
24
24
|
* - credentialStoreFrom/authContextFrom(auth.ts):PiAiAdapter 必需 auth 注入。
|
|
25
25
|
* npm 发布形态(lib/index.js 单 bundle + 无 src/)两者都拿不到:dsh 树优先
|
|
@@ -76,7 +76,7 @@ import { type ResolverDeps, resolveProfilesFallback } from './profiles.ts'
|
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* deepseek 适配器模块表面(可选):dsh-llm-deepseek 的官方 DeepSeekAdapter
|
|
79
|
-
* 与目录解析器 + 匿名用户 id
|
|
79
|
+
* 与目录解析器 + 匿名用户 id。缺失(形状漂移)不拖垮核心
|
|
80
80
|
* 套件——deepseek 类 route 在构建期以明确错误拒绝,pi 路由不受影响。
|
|
81
81
|
* 与核心套件强制同源(杜绝跨源模块混用:brand/LlmError 恒等性敏感)。
|
|
82
82
|
*/
|
|
@@ -161,8 +161,8 @@ function assertKitShape(kit: DshKit, origin: string): void {
|
|
|
161
161
|
const problems: string[] = []
|
|
162
162
|
if (typeof kit.PiAiAdapter !== 'function') problems.push('PiAiAdapter 不是类')
|
|
163
163
|
else {
|
|
164
|
-
//
|
|
165
|
-
//
|
|
164
|
+
// LlmAdapter 抽象面:prepareCall/providerRetryPolicy 为适配器必需 override;
|
|
165
|
+
// models.getModels/getModel 语义
|
|
166
166
|
// (pi-ai 0.84.2 Models.getModel(provider,id)/getModels(provider),
|
|
167
167
|
// 由同源 adapter 内部消费,本插件不直调)。
|
|
168
168
|
for (const method of [
|
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 重注册;写入被校验拒绝时保留旧注册(官方同款护栏);
|
|
@@ -201,8 +201,8 @@ function createAdapter(options: {
|
|
|
201
201
|
profiles,
|
|
202
202
|
resolveApiKey: makeResolveApiKey(ctx, kit),
|
|
203
203
|
resolveAttachments: () => ctx.get('attachments'),
|
|
204
|
-
//
|
|
205
|
-
//
|
|
204
|
+
// 官方 auth 助手只在 src 子路径导出:登录/OAuth 类 provider 与 pi-ai 自有
|
|
205
|
+
// 凭据写入的落点。dsh 树 dev 布局(src/auth.ts 存在)时为官方助手,
|
|
206
206
|
// 其余形态为内联等价实现(resolve-dsh.ts 探测,见 auth-inline.ts)。
|
|
207
207
|
auth: {
|
|
208
208
|
credentials: kit.auth.credentialStoreFrom(ctx),
|
|
@@ -372,7 +372,7 @@ export async function startRuntime(
|
|
|
372
372
|
): Promise<LlmPiRuntime> {
|
|
373
373
|
const logger = ctx.logger('llm-pi')
|
|
374
374
|
// cordis 行级 config 可能未经 schema 解析(insert 行无 config 键时为原始空对象),
|
|
375
|
-
// 在此统一规范化;
|
|
375
|
+
// 在此统一规范化;loader 解析出的 volatile 活动字段形态直接透传
|
|
376
376
|
// (hasVolatileRefs 辨识——重复校验会把引用再包一层、破坏 loader 原位提交)。
|
|
377
377
|
const fields: LlmPiConfigFields = hasVolatileRefs(rawConfig)
|
|
378
378
|
? (rawConfig as LlmPiConfigFields)
|
|
@@ -434,7 +434,7 @@ export async function startRuntime(
|
|
|
434
434
|
ensureDirectory()
|
|
435
435
|
void copyWriter.ensureOfficialCopy()
|
|
436
436
|
|
|
437
|
-
//
|
|
437
|
+
// 替代 installSection(validate/setSource/onChange 三钩子):
|
|
438
438
|
// - 活动引用原位提交 → volatile-update 触发快照换代与重注册(原 onChange 体);
|
|
439
439
|
// - internal/config waterfall 承接写入校验(原 validate;官方 llm-pi-ai
|
|
440
440
|
// 同款):configEditor.edit 落盘前先跑 waterfall,handler throw 即拒绝整笔写入。
|