@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.
@@ -3,11 +3,11 @@
3
3
  *
4
4
  * 官方 credentialStoreFrom/authContextFrom 仅在 @deepseek-ai/dsh-llm-pi-ai 的
5
5
  * src/auth.ts 子路径导出(包根只导出 recordKeyFor),而 npm 发布形态不携带
6
- * src/(lib/index.js 单 bundle)——dsh 树 dev 布局优先经 src 子路径走官方
7
- * 实现(见 resolve-dsh.ts 探测),本文件在其余形态(npm 布局的 dsh 树 /
6
+ * src/(lib/index.js 单 bundle)——源码布局优先经 src 子路径走官方实现
7
+ * (见 resolve-dsh.ts 探测),本文件在其余形态(npm 布局的 dsh 树 /
8
8
  * vendored 兜底)下按官方语义逐行等价实现,并经 assertKitShape 自检兜底。
9
9
  * recordKeyFor 由调用方注入(与 PiAiAdapter 同源模块的包根导出,保证记录
10
- * 键格式与官方写入方一致)。
10
+ * 键格式与官方写入方一致);记录作用域不手抄,由它现场推导(见 recordScopeOf)。
11
11
  * @module llm-pi/auth-inline
12
12
  */
13
13
  import { access } from 'node:fs/promises'
@@ -36,9 +36,6 @@ import type {
36
36
  CredentialStore,
37
37
  } from '@earendil-works/pi-ai'
38
38
 
39
- /** 官方 RECORD_SCOPE 常量(llm-pi-ai/src/auth.ts)——读写作用域必须与官方一致。 */
40
- export const INLINE_RECORD_SCOPE = 'llm-pi-ai'
41
-
42
39
  /** 官方 jsonImage:pi-ai 凭据里显式 undefined 成员 JSON 序列化为缺省/数组 null。 */
43
40
  function jsonImage(value: unknown): unknown {
44
41
  if (Array.isArray(value))
@@ -94,15 +91,31 @@ function writableStore(ctx: Context): CredentialProvider {
94
91
  return credentials
95
92
  }
96
93
 
94
+ /** 作用域推导的探针 provider id(合法记录段,故 recordKeyFor 给出完整键)。 */
95
+ const RECORD_SCOPE_PROBE_ID = 'record-scope-probe'
96
+
97
+ /**
98
+ * 记录作用域的现场推导(**不手抄**官方常量):`recordKeyFor` 由与 PiAiAdapter
99
+ * 同源的官方包根注入,其产物 `scope/id` 的 scope 段就是官方写入方使用的作用域。
100
+ * 官方改作用域时此处自动跟随——硬编码镜像一旦落后,{@link credentialStoreFrom}
101
+ * 的 list() 会把本插件自己的登录记录判成他人的而凭空消失,且无任何报错
102
+ * (与 compat 门控表手抄镜像同一类故障,见 docs/repo/事故记录.md)。
103
+ */
104
+ function recordScopeOf(recordKeyFor: (providerId: string) => CredentialKey): string {
105
+ return credentialKeyScope(recordKeyFor(RECORD_SCOPE_PROBE_ID))
106
+ }
107
+
97
108
  /**
98
109
  * pi-ai `CredentialStore`(官方 credentialStoreFrom 等价实现)。
99
110
  * 路由键任意而记录 id 受语法约束:语法外的 id 读答"未存储"、删除无事可做、
100
- * modify 拒绝(写不落地不能报成功)。
111
+ * modify 拒绝(写不落地不能报成功)。list() 的归属判定用现场推导的作用域
112
+ * ({@link recordScopeOf}),不比对硬编码常量。
101
113
  */
102
114
  export function credentialStoreFrom(
103
115
  ctx: Context,
104
116
  recordKeyFor: (providerId: string) => CredentialKey,
105
117
  ): CredentialStore {
118
+ const recordScope = recordScopeOf(recordKeyFor)
106
119
  return {
107
120
  async read(providerId) {
108
121
  const credentials = ctx.get('credentials')
@@ -114,7 +127,7 @@ export function credentialStoreFrom(
114
127
  const stored = (await ctx.get('credentials')?.listRecords()) ?? []
115
128
  const mine: CredentialInfo[] = []
116
129
  for (const entry of stored) {
117
- if (credentialKeyScope(entry.key) !== INLINE_RECORD_SCOPE) continue
130
+ if (credentialKeyScope(entry.key) !== recordScope) continue
118
131
  mine.push({
119
132
  providerId: credentialKeyId(entry.key),
120
133
  type: entry.kind === 'api-key' ? 'api_key' : 'oauth',
package/src/client/api.ts CHANGED
@@ -26,7 +26,14 @@ export interface WireKitInfo {
26
26
  warnings: string[]
27
27
  error?: string
28
28
  }
29
- diagnostics: string[]
29
+ diagnostics: WireDiagnostic[]
30
+ }
31
+
32
+ /** 套件诊断条目(服务端 KitDiagnostic 的镜像)。 */
33
+ export interface WireDiagnostic {
34
+ /** info = 打包形态说明(非缺陷);degradation = 回退与逐项降级。 */
35
+ level: 'info' | 'degradation'
36
+ message: string
30
37
  }
31
38
 
32
39
  /** 内置 provider 条目。 */
@@ -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。
@@ -27,7 +27,8 @@ const ownZh = {
27
27
  officialCopyPending: '尚未生成',
28
28
  officialCopyFailed: '生成失败:',
29
29
  officialCopyRoutes: '个 route',
30
- runtimeDiagnostics: '诊断',
30
+ runtimeFormNotes: '形态说明',
31
+ runtimeDiagnostics: '降级诊断',
31
32
  runtimeVersionUnknown: '未知',
32
33
  runtimeCatalogUnit: '个 provider /',
33
34
  runtimeCatalogModels: '个模型',
@@ -168,7 +169,8 @@ const ownEn = {
168
169
  officialCopyPending: 'not generated yet',
169
170
  officialCopyFailed: 'generation failed: ',
170
171
  officialCopyRoutes: 'routes',
171
- runtimeDiagnostics: 'Diagnostics',
172
+ runtimeFormNotes: 'Packaging notes',
173
+ runtimeDiagnostics: 'Degradations',
172
174
  runtimeVersionUnknown: 'unknown',
173
175
  runtimeCatalogUnit: 'providers /',
174
176
  runtimeCatalogModels: 'models',
@@ -3,15 +3,16 @@
3
3
  *
4
4
  * 状态行回答的是"我现在到底在跑哪一份 pi-ai"——插件按文件路径动态加载已装 dsh 树
5
5
  * 里的套件(见 resolve-dsh.ts),manifest 上写的版本**不是**生效版本。这里显示
6
- * 生效版本、安装树、目录规模与数据日期、可服务协议、compat 表来源,以及逐项降级
7
- * 诊断;版本超出验证区间时给出提示(提示而非阻断)。
6
+ * 生效版本、安装树、目录规模与数据日期、可服务协议、compat 表来源,以及诊断
7
+ * (按「形态说明 / 降级诊断」分块,避免恒定事实淹没真实降级);版本超出验证区间
8
+ * 时给出提示(提示而非阻断)。
8
9
  * @module llm-pi/client/root-fields
9
10
  */
10
11
 
11
12
  import { CheckRow } from '@dsh-plus/shared/client'
12
13
  import type { ReactElement } from 'react'
13
14
 
14
- import type { WireKitInfo } from './api.ts'
15
+ import type { WireDiagnostic, WireKitInfo } from './api.ts'
15
16
  import type { Draft } from './draft.ts'
16
17
  import type { Translate } from './i18n.ts'
17
18
 
@@ -44,6 +45,25 @@ function officialCopyText(kit: WireKitInfo, t: Translate): string {
44
45
  return `${copy.path} · ${copy.routes} ${t('officialCopyRoutes')} · ${formatTime(copy.updatedAt, t('runtimeVersionUnknown'))}`
45
46
  }
46
47
 
48
+ /**
49
+ * 诊断分块:形态说明与降级分开渲染——「官方 src 不随 npm 发布」这类恒定事实
50
+ * 永不会消失,混在一起会把真正的降级淹掉。空块不渲染。
51
+ */
52
+ function DiagnosticBlock(props: {
53
+ label: string
54
+ level: WireDiagnostic['level']
55
+ items: readonly WireDiagnostic[]
56
+ }): ReactElement | null {
57
+ const messages = props.items.filter((item) => item.level === props.level).map((m) => m.message)
58
+ if (messages.length === 0) return null
59
+ return (
60
+ <>
61
+ <p className="lpc-catDetailLabel">{props.label}</p>
62
+ <pre className="lpc-catJson">{messages.join('\n')}</pre>
63
+ </>
64
+ )
65
+ }
66
+
47
67
  export interface RootFieldsProps {
48
68
  t: Translate
49
69
  draft: Draft
@@ -96,8 +116,12 @@ export function RootFields(props: RootFieldsProps): ReactElement {
96
116
  ) : null}
97
117
  {kit.diagnostics.length > 0 ? (
98
118
  <>
99
- <p className="lpc-catDetailLabel">{t('runtimeDiagnostics')}</p>
100
- <pre className="lpc-catJson">{kit.diagnostics.join('\n')}</pre>
119
+ <DiagnosticBlock label={t('runtimeFormNotes')} level="info" items={kit.diagnostics} />
120
+ <DiagnosticBlock
121
+ label={t('runtimeDiagnostics')}
122
+ level="degradation"
123
+ items={kit.diagnostics}
124
+ />
101
125
  </>
102
126
  ) : null}
103
127
  </>
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 打崩。