@dsh-plus/llm-pi 0.1.5 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/llm-pi",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "dsh-plus service+ui plugin: 基于 PiAiAdapter 的自定义 LLM 路由(全量 compat、模型继承、models.dev 目录兜底)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -36,25 +36,30 @@
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.1",
39
- "@deepseek-ai/schemastery": "^3.18.1",
40
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
41
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
39
+ "@deepseek-ai/dsh-anonymous-user-id": "^0.1.1-rc.2",
42
40
  "@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
43
41
  "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.8",
42
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
44
43
  "@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.8",
44
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
45
+ "@deepseek-ai/dsh-llm-deepseek": "^0.1.1-rc.2",
45
46
  "@deepseek-ai/dsh-llm-pi-ai": "^0.1.0-rc.8",
47
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
48
+ "@deepseek-ai/schemastery": "^3.18.1",
46
49
  "@earendil-works/pi-ai": "^0.82.1"
47
50
  },
48
51
  "devDependencies": {
49
52
  "@deepseek-ai/cordis": "4.0.1",
53
+ "@deepseek-ai/dsh-anonymous-user-id": "0.1.1-rc.2",
54
+ "@deepseek-ai/dsh-credentials": "0.1.1-rc.2",
55
+ "@deepseek-ai/dsh-home-paths": "0.1.1-rc.2",
56
+ "@deepseek-ai/dsh-host-webserver": "0.1.1-rc.2",
57
+ "@deepseek-ai/dsh-launch-environment": "0.1.1-rc.2",
58
+ "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
59
+ "@deepseek-ai/dsh-llm-deepseek": "0.1.1-rc.2",
60
+ "@deepseek-ai/dsh-llm-pi-ai": "0.1.1-rc.2",
61
+ "@deepseek-ai/dsh-settings": "0.1.1-rc.2",
50
62
  "@deepseek-ai/schemastery": "3.18.1",
51
- "@deepseek-ai/dsh-settings": "0.1.0-rc.8",
52
- "@deepseek-ai/dsh-llm": "0.1.0-rc.8",
53
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.8",
54
- "@deepseek-ai/dsh-home-paths": "0.1.0-rc.8",
55
- "@deepseek-ai/dsh-launch-environment": "0.1.0-rc.8",
56
- "@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.8",
57
- "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.8",
58
63
  "@earendil-works/pi-ai": "0.82.1",
59
64
  "@types/react": "~18.3.1",
60
65
  "react": "^18.2.0"
@@ -19,7 +19,12 @@ export interface ModelBase {
19
19
  compat?: Record<string, unknown>
20
20
  contextWindow?: number
21
21
  maxTokens?: number
22
- cost?: { input: number; output: number; cacheRead: number; cacheWrite: number }
22
+ cost?: {
23
+ input: number
24
+ output: number
25
+ cacheRead: number
26
+ cacheWrite: number
27
+ }
23
28
  headers?: Record<string, string>
24
29
  }
25
30
 
@@ -40,7 +45,11 @@ export function builtinModelIds(kit: DshKit, provider: string): string[] {
40
45
  }
41
46
 
42
47
  /** 查单个内置模型为继承 base;未命中返回 undefined。 */
43
- export function builtinModelBase(kit: DshKit, provider: string, modelId: string): ModelBase | undefined {
48
+ export function builtinModelBase(
49
+ kit: DshKit,
50
+ provider: string,
51
+ modelId: string,
52
+ ): ModelBase | undefined {
44
53
  if (!hasBuiltinProvider(kit, provider)) return undefined
45
54
  const model = kit.getBuiltinModels(provider).find((m) => m.id === modelId)
46
55
  if (model === undefined) return undefined
@@ -50,8 +59,12 @@ export function builtinModelBase(kit: DshKit, provider: string, modelId: string)
50
59
  baseUrl: model.baseUrl,
51
60
  input: [...model.input],
52
61
  reasoning: model.reasoning,
53
- ...(model.thinkingLevelMap === undefined ? {} : { thinkingLevelMap: { ...model.thinkingLevelMap } }),
54
- ...(model.compat === undefined ? {} : { compat: { ...(model.compat as Record<string, unknown>) } }),
62
+ ...(model.thinkingLevelMap === undefined
63
+ ? {}
64
+ : { thinkingLevelMap: { ...model.thinkingLevelMap } }),
65
+ ...(model.compat === undefined
66
+ ? {}
67
+ : { compat: { ...(model.compat as Record<string, unknown>) } }),
55
68
  contextWindow: model.contextWindow,
56
69
  maxTokens: model.maxTokens,
57
70
  cost: { ...model.cost },
@@ -15,9 +15,10 @@
15
15
  * 视觉等模态须用户在条目上显式声明(防 over-claiming 导致会话重复失败请求)。
16
16
  * @module llm-pi/catalog/models-dev
17
17
  */
18
+
19
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
18
20
  import { request as httpRequest } from 'node:http'
19
21
  import { request as httpsRequest } from 'node:https'
20
- import { mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs'
21
22
  import { dirname } from 'node:path'
22
23
 
23
24
  import HttpsProxyAgentModule from 'https-proxy-agent'
@@ -73,7 +74,8 @@ function fetchJson(url: string, proxy: string, timeoutMs: number): Promise<JsonR
73
74
  return new Promise((resolve, reject) => {
74
75
  const target = new URL(url)
75
76
  const request = target.protocol === 'https:' ? httpsRequest : httpRequest
76
- const agent = target.protocol === 'https:' && proxy.length > 0 ? new HttpsProxyAgent(proxy) : undefined
77
+ const agent =
78
+ target.protocol === 'https:' && proxy.length > 0 ? new HttpsProxyAgent(proxy) : undefined
77
79
  const req = request(
78
80
  url,
79
81
  { agent, timeout: timeoutMs, headers: { accept: 'application/json' } },
@@ -89,7 +91,10 @@ function fetchJson(url: string, proxy: string, timeoutMs: number): Promise<JsonR
89
91
  chunks.push(chunk)
90
92
  })
91
93
  response.on('end', () => {
92
- resolve({ status: response.statusCode ?? 0, body: Buffer.concat(chunks).toString('utf8') })
94
+ resolve({
95
+ status: response.statusCode ?? 0,
96
+ body: Buffer.concat(chunks).toString('utf8'),
97
+ })
93
98
  })
94
99
  },
95
100
  )
@@ -108,7 +113,8 @@ function toModelBase(entry: ModelsDevModelEntry): ModelBase {
108
113
  const base: ModelBase = {}
109
114
  if (typeof entry.name === 'string' && entry.name.length > 0) base.name = entry.name
110
115
  const context = entry.limit?.context
111
- if (typeof context === 'number' && Number.isInteger(context) && context > 0) base.contextWindow = context
116
+ if (typeof context === 'number' && Number.isInteger(context) && context > 0)
117
+ base.contextWindow = context
112
118
  const output = entry.limit?.output
113
119
  if (typeof output === 'number' && Number.isInteger(output) && output > 0) base.maxTokens = output
114
120
  base.reasoning = entry.reasoning === true
@@ -178,7 +184,7 @@ export class ModelsDevSource {
178
184
 
179
185
  /** 全部 provider id(UI extends 选择器用)。 */
180
186
  providerIds(): string[] {
181
- return Object.keys(this.document ?? {})
187
+ return Object.keys(this.document ?? {})
182
188
  }
183
189
 
184
190
  /** 某 provider 的模型 id 列表(UI extends 选择器用)。 */
@@ -209,7 +215,8 @@ export class ModelsDevSource {
209
215
  private loadCache(): void {
210
216
  try {
211
217
  const raw = JSON.parse(readFileSync(this.cacheFile, 'utf8')) as CacheFile
212
- if (!isDocument(raw.data) || typeof raw.fetchedAt !== 'string') throw new Error('缓存形状非法')
218
+ if (!isDocument(raw.data) || typeof raw.fetchedAt !== 'string')
219
+ throw new Error('缓存形状非法')
213
220
  this.document = raw.data
214
221
  this.fetchedAt = raw.fetchedAt
215
222
  } catch {
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 官方模型目录数据源:adapter: deepseek 的 route 的继承 base。
3
+ *
4
+ * 目录取自 dsh-llm-deepseek 的 resolveAdapterOptions({}, undefined)——
5
+ * 即官方 DEFAULT_MODELS 经 schema 默认值补全后的形态(含视觉模型的
6
+ * inputModalities / imagePixelBudget / imageMaxBytes),跟随 dsh 树升级
7
+ * 自动更新,不在本仓库复制一份硬编码副本。
8
+ * @module llm-pi/catalog/official
9
+ */
10
+ import type { DshKit } from '../resolve-dsh.ts'
11
+
12
+ /** 官方目录可提供的可继承模型字段(DeepSeekCatalogModel 的可继承子集)。 */
13
+ export interface OfficialModelBase {
14
+ name?: string
15
+ contextWindow?: number
16
+ maxTokens?: number
17
+ input?: ('text' | 'image')[]
18
+ imagePixelBudget?: number
19
+ imageMaxBytes?: number
20
+ imageDetail?: 'auto' | 'low'
21
+ }
22
+
23
+ interface OfficialCatalog {
24
+ baseUrl: string
25
+ models: Map<string, OfficialModelBase>
26
+ }
27
+
28
+ /** 按 kit 备忘(kit 实例在进程内唯一,目录随 dsh 树版本固定)。 */
29
+ const cache = new WeakMap<DshKit, OfficialCatalog>()
30
+
31
+ function catalogOf(kit: DshKit): OfficialCatalog {
32
+ const hit = cache.get(kit)
33
+ if (hit !== undefined) return hit
34
+ if (kit.deepseek === undefined) {
35
+ throw new Error('llm-pi: 官方模型目录需要 dsh-llm-deepseek(当前运行时套件不含)')
36
+ }
37
+ const resolved = kit.deepseek.resolveAdapterOptions({} as never, undefined)
38
+ const models = new Map<string, OfficialModelBase>()
39
+ for (const model of resolved.models) {
40
+ models.set(model.id, {
41
+ ...(model.name === undefined ? {} : { name: model.name }),
42
+ ...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }),
43
+ ...(model.maxTokens === undefined ? {} : { maxTokens: model.maxTokens }),
44
+ ...(model.inputModalities === undefined
45
+ ? {}
46
+ : { input: [...model.inputModalities] as ('text' | 'image')[] }),
47
+ ...(model.imagePixelBudget === undefined ? {} : { imagePixelBudget: model.imagePixelBudget }),
48
+ ...(model.imageMaxBytes === undefined ? {} : { imageMaxBytes: model.imageMaxBytes }),
49
+ ...(model.imageDetail === undefined ? {} : { imageDetail: model.imageDetail }),
50
+ })
51
+ }
52
+ const catalog: OfficialCatalog = { baseUrl: resolved.baseURL, models }
53
+ cache.set(kit, catalog)
54
+ return catalog
55
+ }
56
+
57
+ /** 官方端点(route 配 extends: 'deepseek' 且未显式 baseURL 时的缺省值)。 */
58
+ export function officialBaseUrl(kit: DshKit): string {
59
+ return catalogOf(kit).baseUrl
60
+ }
61
+
62
+ /** 查单个官方模型为继承 base;未命中返回 undefined。 */
63
+ export function officialModelBase(kit: DshKit, modelId: string): OfficialModelBase | undefined {
64
+ const base = catalogOf(kit).models.get(modelId)
65
+ return base === undefined ? undefined : { ...base }
66
+ }
67
+
68
+ /** 官方目录的全部模型 id(route 级 extends: 'deepseek' 的全量继承与诊断用)。 */
69
+ export function officialModelIds(kit: DshKit): string[] {
70
+ return [...catalogOf(kit).models.keys()]
71
+ }
package/src/client/api.ts CHANGED
@@ -23,7 +23,12 @@ export interface WireProvider {
23
23
  defaultMaxTokens?: number
24
24
  defaultInput?: string[]
25
25
  reasoning?: string
26
- thinkingBudgets?: { minimal: number; low: number; medium: number; high: number }
26
+ thinkingBudgets?: {
27
+ minimal: number
28
+ low: number
29
+ medium: number
30
+ high: number
31
+ }
27
32
  cacheRetention?: string
28
33
  transport?: string
29
34
  timeoutMs?: number
@@ -74,14 +79,22 @@ async function parse<T>(res: Response): Promise<T> {
74
79
  }
75
80
 
76
81
  /** 目录查询:provider 为空时只返回该源的 provider 列表。 */
77
- export async function fetchCatalog(provider: string, source: 'builtin' | 'models-dev'): Promise<CatalogResult> {
82
+ export async function fetchCatalog(
83
+ provider: string,
84
+ source: 'builtin' | 'models-dev',
85
+ ): Promise<CatalogResult> {
78
86
  const url = `${ROUTE_CATALOG}?provider=${encodeURIComponent(provider)}&source=${source}`
79
87
  return parse<CatalogResult>(await fetch(url, { credentials: 'same-origin' }))
80
88
  }
81
89
 
82
90
  /** 手动拉取 models.dev 目录:POST /catalog/refresh → 最新快照状态。 */
83
- export async function refreshCatalog(): Promise<{ status: WireModelsDevStatus }> {
91
+ export async function refreshCatalog(): Promise<{
92
+ status: WireModelsDevStatus
93
+ }> {
84
94
  return parse<{ status: WireModelsDevStatus }>(
85
- await fetch(`${ROUTE_CATALOG}/refresh`, { method: 'POST', credentials: 'same-origin' }),
95
+ await fetch(`${ROUTE_CATALOG}/refresh`, {
96
+ method: 'POST',
97
+ credentials: 'same-origin',
98
+ }),
86
99
  )
87
100
  }
@@ -7,11 +7,18 @@
7
7
  * 交互对齐官方卡片与 notify-email:折叠/展开、staged draft、未保存标记。
8
8
  * @module llm-pi/client/card
9
9
  */
10
- import { useEffect, useMemo, useSyncExternalStore, useState, type ReactElement } from 'react'
10
+ import { type ReactElement, useEffect, useMemo, useState, useSyncExternalStore } from 'react'
11
11
 
12
12
  import { SETTINGS_NS } from '../ns.ts'
13
- import { fetchCatalog, refreshCatalog, type ConfigValue, type WireModelsDevStatus } from './api.ts'
14
- import { draftFromValue, emptyProviderDraft, numTextOk, toPatch, type Draft, type ProviderDraft } from './draft.ts'
13
+ import { type ConfigValue, fetchCatalog, refreshCatalog, type WireModelsDevStatus } from './api.ts'
14
+ import {
15
+ type Draft,
16
+ draftFromValue,
17
+ emptyProviderDraft,
18
+ numTextOk,
19
+ type ProviderDraft,
20
+ toPatch,
21
+ } from './draft.ts'
15
22
  import { CheckRow, TextField } from './fields.tsx'
16
23
  import type { Scope, SettingsApi } from './scope.ts'
17
24
  import { ProvidersSection } from './views/providers.tsx'
@@ -75,7 +82,8 @@ export function LlmPiCard(props: CardProps): ReactElement | null {
75
82
 
76
83
  const dirty = useMemo(
77
84
  () =>
78
- value !== undefined && draft !== null &&
85
+ value !== undefined &&
86
+ draft !== null &&
79
87
  JSON.stringify(toPatch(draft)) !== JSON.stringify(toPatch(draftFromValue(value))),
80
88
  [value, draft],
81
89
  )
@@ -90,16 +98,26 @@ export function LlmPiCard(props: CardProps): ReactElement | null {
90
98
  }, [draft])
91
99
 
92
100
  if (value === undefined || draft === null) {
93
- return <li className="lpc-card"><p className="lpc-readOnly">{t('loading')}</p></li>
101
+ return (
102
+ <li className="lpc-card">
103
+ <p className="lpc-readOnly">{t('loading')}</p>
104
+ </li>
105
+ )
94
106
  }
95
107
 
96
108
  const setProvider = (route: string, patch: Partial<ProviderDraft>): void => {
97
109
  const current = draft.providers[route] ?? emptyProviderDraft()
98
- setDraft({ ...draft, providers: { ...draft.providers, [route]: { ...current, ...patch } } })
110
+ setDraft({
111
+ ...draft,
112
+ providers: { ...draft.providers, [route]: { ...current, ...patch } },
113
+ })
99
114
  setStatus(IDLE_STATUS)
100
115
  }
101
116
  const onAddRoute = (key: string): void => {
102
- setDraft({ ...draft, providers: { ...draft.providers, [key]: emptyProviderDraft() } })
117
+ setDraft({
118
+ ...draft,
119
+ providers: { ...draft.providers, [key]: emptyProviderDraft() },
120
+ })
103
121
  setStatus(IDLE_STATUS)
104
122
  }
105
123
  const onRemoveRoute = (route: string): void => {
@@ -111,11 +129,12 @@ export function LlmPiCard(props: CardProps): ReactElement | null {
111
129
  const onSave = (): void => {
112
130
  setSaving(true)
113
131
  const revision = scope.getSnapshot().revision
114
- api.replace({
115
- ns: SETTINGS_NS,
116
- section: toPatch(draft) as unknown as Record<string, unknown>,
117
- ...(revision !== undefined ? { expectedRevision: revision } : {}),
118
- })
132
+ api
133
+ .replace({
134
+ ns: SETTINGS_NS,
135
+ section: toPatch(draft) as unknown as Record<string, unknown>,
136
+ ...(revision !== undefined ? { expectedRevision: revision } : {}),
137
+ })
119
138
  .then(async (response) => {
120
139
  if (!response.result.ok) {
121
140
  throw new Error(response.result.error?.message ?? t('saveFailed'))
@@ -170,7 +189,11 @@ export function LlmPiCard(props: CardProps): ReactElement | null {
170
189
  </button>
171
190
  {open ? (
172
191
  <div className="lpc-body">
173
- {disabled ? <p className="lpc-readOnly" role="status">{t('readOnly')}</p> : null}
192
+ {disabled ? (
193
+ <p className="lpc-readOnly" role="status">
194
+ {t('readOnly')}
195
+ </p>
196
+ ) : null}
174
197
  <CheckRow
175
198
  id="lpc-enabled"
176
199
  label={t('enabled')}
@@ -217,9 +240,13 @@ export function LlmPiCard(props: CardProps): ReactElement | null {
217
240
  setStatus(IDLE_STATUS)
218
241
  }}
219
242
  />
220
- <p className="lpc-statusRow">{t('kitSource')}:{kitSource ?? ''}</p>
243
+ <p className="lpc-statusRow">
244
+ {t('kitSource')}:{kitSource ?? ''}
245
+ </p>
221
246
  <div className="lpc-statusRow">
222
- <span>{t('modelsDevStatus')}:{modelsDevText(modelsDevStatus, t)}</span>
247
+ <span>
248
+ {t('modelsDevStatus')}:{modelsDevText(modelsDevStatus, t)}
249
+ </span>
223
250
  <button
224
251
  type="button"
225
252
  className="lpc-btn lpc-btnGhost lpc-btnSmall lpc-refreshBtn"
@@ -240,17 +267,27 @@ export function LlmPiCard(props: CardProps): ReactElement | null {
240
267
  />
241
268
  <div className="lpc-footer">
242
269
  {status.kind !== 'idle' ? (
243
- <p className={`lpc-status${status.kind === 'error' ? ' lpc-statusError' : ''}`} role="status">
270
+ <p
271
+ className={`lpc-status${status.kind === 'error' ? ' lpc-statusError' : ''}`}
272
+ role="status"
273
+ >
244
274
  {status.text}
245
275
  </p>
246
276
  ) : null}
247
- <button type="button" className="lpc-btn lpc-btnGhost" disabled={!dirty || saving}
248
- onClick={onDiscard}>
277
+ <button
278
+ type="button"
279
+ className="lpc-btn lpc-btnGhost"
280
+ disabled={!dirty || saving}
281
+ onClick={onDiscard}
282
+ >
249
283
  {t('discard')}
250
284
  </button>
251
- <button type="button" className="lpc-btn lpc-btnPrimary"
285
+ <button
286
+ type="button"
287
+ className="lpc-btn lpc-btnPrimary"
252
288
  disabled={!dirty || invalid || saving || disabled}
253
- onClick={onSave}>
289
+ onClick={onSave}
290
+ >
254
291
  {t(saving ? 'saving' : 'save')}
255
292
  </button>
256
293
  </div>
@@ -64,7 +64,12 @@ interface ClientContext {
64
64
  * (loopback 或 tailnet 信任域名)下行为一致。
65
65
  */
66
66
  function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
67
- let state: ScopeSnapshot = { status: 'loading', value: undefined, revision: undefined, writable: false }
67
+ let state: ScopeSnapshot = {
68
+ status: 'loading',
69
+ value: undefined,
70
+ revision: undefined,
71
+ writable: false,
72
+ }
68
73
  const listeners = new Set<() => void>()
69
74
  let generation = 0
70
75
  const publish = (next: ScopeSnapshot): void => {
@@ -73,7 +78,7 @@ function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
73
78
  }
74
79
  const load = async (): Promise<void> => {
75
80
  const gen = ++generation
76
- let response
81
+ let response: Awaited<ReturnType<SettingsApi['describe']>>
77
82
  try {
78
83
  response = await api.describe({})
79
84
  } catch {
@@ -83,10 +88,20 @@ function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
83
88
  const writable = response.result.value?.writable ?? false
84
89
  const view = response.result.value?.namespaces.find((candidate) => candidate.ns === ns)
85
90
  if (view === undefined) {
86
- publish({ status: 'unavailable', value: undefined, revision: undefined, writable })
91
+ publish({
92
+ status: 'unavailable',
93
+ value: undefined,
94
+ revision: undefined,
95
+ writable,
96
+ })
87
97
  return
88
98
  }
89
- publish({ status: 'ready', value: view.value, revision: view.revision, writable })
99
+ publish({
100
+ status: 'ready',
101
+ value: view.value,
102
+ revision: view.revision,
103
+ writable,
104
+ })
90
105
  }
91
106
  const refresh = (namespace?: unknown): void => {
92
107
  if (namespace !== undefined && namespace !== ns) return
@@ -118,7 +133,6 @@ function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
118
133
  }
119
134
 
120
135
  export function apply(ctx: Context): void {
121
-
122
136
  const c = ctx as unknown as ClientContext
123
137
  const tag = injectStyle()
124
138
  c.effect(
@@ -127,10 +141,7 @@ export function apply(ctx: Context): void {
127
141
  },
128
142
  'llm-pi: style',
129
143
  )
130
- c.effect(
131
- () => c.locale.register(NS, { zh, en }),
132
- 'llm-pi: locale',
133
- )
144
+ c.effect(() => c.locale.register(NS, { zh, en }), 'llm-pi: locale')
134
145
  const api = c.get('connection').api.settings
135
146
  const scope = createApiScope(api, SETTINGS_NS, c)
136
147
  c.slots.inject('settings.plugin.item', () =>
@@ -6,7 +6,11 @@
6
6
  */
7
7
 
8
8
  /** 协议枚举(来源:config.ts PROTOCOL_IDS)。 */
9
- export const PROTOCOL_IDS = ['openai-completions', 'openai-responses', 'anthropic-messages'] as const
9
+ export const PROTOCOL_IDS = [
10
+ 'openai-completions',
11
+ 'openai-responses',
12
+ 'anthropic-messages',
13
+ ] as const
10
14
 
11
15
  /** thinking 档位(来源:config.ts THINKING_LEVELS)。 */
12
16
  export const THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const