@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 CHANGED
@@ -184,7 +184,7 @@ window.__ModuleLoader__.load({
184
184
  unsaved: "未保存",
185
185
  loading: "加载中…",
186
186
  unavailable: "配置服务不可用。",
187
- readOnly: "当前部署无 settings provider,配置为只读;请编辑 settings.yaml。",
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. Edit settings.yaml.",
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
- * 服务端直接以字面量注册(0.1.2-alpha.2 起 dsh-settings 对命名空间做编译期
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 命名空间(字面量即合法命名空间,0.1.2-alpha.2 起编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
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
- /** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
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
- *(用户层,经 dsh-settings-file 持久化到 $DSH_HOME/settings.yaml)共用同一
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 命名空间(字面量即合法命名空间,0.1.2-alpha.2 起编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
38
+ /** settings 命名空间(字面量即合法命名空间,编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
39
39
  const SETTINGS_NS = "dsh-plus-llm-pi";
40
40
  /**
41
- * 官方适配器不可用/推导失败时的协议兜底三元组(自 0.1.2-alpha 线以来的稳定集)。
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(0.1.2-alpha.1 起官方必需字段,旧配置免改)"),
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
- * 历史教训(0.1.5-rc.1):compat 门控曾是官方 catalog.ts 的手抄镜像,官方扩容
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
- /** 官方门控表不可解析时的最后已知快照(0.1.5-rc.2 实测值)。 */
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
- deferredToolsMode: "withhold",
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
- supportsToolReferences: "withhold",
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(0.1.2-alpha.1 基线)");
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 构建器(0.1.2-alpha.2 复核兼容):插件 Config → 官方 schema 形状的
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
- * 0.1.2-alpha 线的新面(0.1.2-alpha.2 复核包根仍未导出、仅 src 子路径有):
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.0",
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"
35
+ "@dsh-plus/shared": "0.1.36"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.5-alpha.1",
@@ -7,8 +7,8 @@
7
7
  *
8
8
  * 类型说明:浏览器半只用到 slots/locale/remote 的很窄一面,
9
9
  * scope 与基础控件走 @dsh-plus/shared/client 套件(收编自本插件原实现)。
10
- * 配置读写经 ctx.remote.settings 直连(0.1.2-alpha.1 起 connection.api.settings
11
- * 已移除;0.1.7 起 settingsScope 服务亦删除(configForms 取代)——插件卡片自始
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
- * 严重得多(0.1.5-rc.1 教训:静默功能缺失、无任何报错)。
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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 自定义端点 HTTP 通道:配置读写走官方 remote.settings 直连
3
- * (0.1.2-alpha.1 起第三方命名空间全量开放),这里只提供浏览器半拿不到的两样东西:
3
+ * (第三方命名空间全量开放),这里只提供浏览器半拿不到的两样东西:
4
4
  * - 运行期套件事实(生效版本、协议集合、目录规模、逐项降级诊断);
5
5
  * - pi-ai 内置目录的搜索/筛选/分页(浏览器半读不到 pi-ai 包)。
6
6
  *
package/src/config.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
3
- *(用户层,经 dsh-settings-file 持久化到 $DSH_HOME/settings.yaml)共用同一
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 命名空间(字面量即合法命名空间,0.1.2-alpha.2 起编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
20
+ /** settings 命名空间(字面量即合法命名空间,编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
21
21
  export const SETTINGS_NS = NS_LITERAL
22
22
 
23
23
  /**
24
- * 官方适配器不可用/推导失败时的协议兜底三元组(自 0.1.2-alpha 线以来的稳定集)。
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
- // 0.1.7:根字段全 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
325
+ // 根字段全 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
337
326
  // loader 原位提交活动引用;输出面标注活动字段声明面(结构兼容 volatile 引用),
338
327
  // 平面消费契约保持 LlmPiConfig 接口不变(服务侧经 unwrapVolatile 解包现取)。
339
- /** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
328
+ /** 活动字段形态(loader 解析产物:volatile 字段为活动引用)。 */
340
329
  export type LlmPiConfigFields = VolatileFields<LlmPiConfig>
341
330
 
342
331
  export const Config: z<LlmPiConfigInput, LlmPiConfigFields> = z.object({
@@ -11,7 +11,7 @@
11
11
  * 请求生效);retryPolicy 是注册期捕获事实,变化时 handle.replace 原地重注册
12
12
  * (官方同款模式);route 移除即 dispose 释放路由名。
13
13
  *
14
- * 0.1.2-alpha 线:DeepSeekAdapterOptions 必需 prepareExtensions(官方接线
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
- // 0.1.2-alpha 线必需:官方扩展注册表(deepseekLlmApiExtensions)缺席时
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 兜底源已随 0.1.43 移除——
6
+ * 必填字段由 route 级配置或兜底给出。models.dev 兜底源已移除——
7
7
  * 版本漂移靠「现场推导 + 状态行显示生效版本」处理,不引入第二份可能过期的目录。
8
8
  *
9
9
  * 引用语法:`"provider/model"` 显式引用;裸 `"model"` 随 route 级 extends 源;
package/src/ns.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
3
- * 服务端直接以字面量注册(0.1.2-alpha.2 起 dsh-settings 对命名空间做编译期
3
+ * 服务端直接以字面量注册(dsh-settings 对命名空间做编译期
4
4
  * 语法校验,见 config.ts);浏览器半把它作为配置卡片的注册键
5
5
  * (插件页配置卡片挂在 row/bundle config 槽位上,与命名空间解耦,
6
6
  * 见 shared/config-slots),
@@ -10,7 +10,7 @@
10
10
  * 3. **模型条目字段集**:Config schema 的 `providers.*.models` 键集——继承时按此
11
11
  * 透传 pi-ai 目录的同名字段,官方将来新增模型级字段即自动跟随。
12
12
  *
13
- * 历史教训(0.1.5-rc.1):compat 门控曾是官方 catalog.ts 的手抄镜像,官方扩容
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
- /** 官方门控表不可解析时的最后已知快照(0.1.5-rc.2 实测值)。 */
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
- deferredToolsMode: 'withhold',
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
- supportsToolReferences: 'withhold',
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',
@@ -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(0.1.2-alpha.1 基线)',
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 构建器(0.1.2-alpha.2 复核兼容):插件 Config → 官方 schema 形状的
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
- // 官方 0.1.5-alpha.2(llm-pi-ai)adapter 读取 profile.modelErrors/catalogError:
601
+ // 官方 llm-pi-ai adapter 读取 profile.modelErrors/catalogError:
610
602
  // 本 fallback 对条目严格校验、坏条目整体拒绝(不产生"条目级不可服务"的
611
603
  // deferred 诊断),故补空表保持与官方 profile 形状一致——缺失会让
612
604
  // PiAiAdapter.modelOf 读 modelErrors.get 时以 TypeError 打崩。
@@ -19,7 +19,7 @@
19
19
  * 模型条目字段集同样现场推导(official-surface.ts)。生效版本只用于状态行显示,
20
20
  * 超出已验证区间也只是提示(kit-meta.ts),永不阻断。
21
21
  *
22
- * 0.1.2-alpha 线的新面(0.1.2-alpha.2 复核包根仍未导出、仅 src 子路径有):
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。缺失(旧版 dsh 树或形状漂移)不拖垮核心
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
- // 0.1.2-alpha.2 的 LlmAdapter 抽象面:prepareCall/providerRetryPolicy 为
165
- // master 适配器必需 override;models.getModels/getModel 语义未变
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 备忘;配置变更经 0.1.7
5
- * volatile 原位提交(loader/volatile-update 换代快照)传播,下一请求生效
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
- // 0.1.2-alpha.2 必需(包根仍未导出官方 auth 助手):登录/OAuth 类 provider 与
205
- // pi-ai 自有凭据写入的落点。dsh 树 dev 布局(src/auth.ts 存在)时为官方助手,
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
- // 在此统一规范化;0.1.7 loader 解析出的 volatile 活动字段形态直接透传
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
- // 0.1.7 替代 installSection(validate/setSource/onChange 三钩子):
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 即拒绝整笔写入。