@dsh-plus/llm-pi 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +33 -13
- package/lib/index.d.ts +2 -2
- package/lib/index.js +140 -47
- package/package.json +2 -2
- package/src/auth-inline.ts +21 -8
- package/src/client/api.ts +8 -1
- package/src/client/client.ts +2 -2
- package/src/client/i18n.ts +4 -2
- package/src/client/root-fields.tsx +29 -5
- package/src/compat.ts +2 -2
- package/src/config-api.ts +1 -1
- package/src/config.ts +6 -17
- package/src/deepseek-routes.ts +2 -2
- package/src/inherit.ts +1 -1
- package/src/ns.ts +1 -1
- package/src/official-surface.ts +45 -4
- package/src/profiles-deepseek.ts +1 -11
- package/src/profiles.ts +2 -10
- package/src/resolve-dsh.ts +110 -46
- package/src/service.ts +20 -14
package/src/auth-inline.ts
CHANGED
|
@@ -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
|
|
7
|
-
*
|
|
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) !==
|
|
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:
|
|
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 条目。 */
|
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/client/i18n.ts
CHANGED
|
@@ -27,7 +27,8 @@ const ownZh = {
|
|
|
27
27
|
officialCopyPending: '尚未生成',
|
|
28
28
|
officialCopyFailed: '生成失败:',
|
|
29
29
|
officialCopyRoutes: '个 route',
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
100
|
-
<
|
|
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
|
-
*
|
|
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 打崩。
|