@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/lib/client.js +1881 -1845
- package/lib/index.d.ts +21 -4
- package/lib/index.js +573 -135
- package/package.json +16 -11
- package/src/catalog/builtin.ts +17 -4
- package/src/catalog/models-dev.ts +13 -6
- package/src/catalog/official.ts +71 -0
- package/src/client/api.ts +17 -4
- package/src/client/card.tsx +57 -20
- package/src/client/client.ts +20 -9
- package/src/client/constants.ts +5 -1
- package/src/client/draft.ts +110 -38
- package/src/client/fields.tsx +3 -1
- package/src/client/i18n.ts +4 -2
- package/src/client/scope.ts +22 -3
- package/src/client/views/compat.tsx +50 -44
- package/src/client/views/models.tsx +26 -7
- package/src/client/views/provider-fields.tsx +15 -3
- package/src/client/views/providers.tsx +31 -26
- package/src/compat.ts +18 -4
- package/src/config.ts +147 -11
- package/src/deepseek-routes.ts +139 -0
- package/src/discovery.ts +63 -11
- package/src/index.ts +6 -3
- package/src/inherit.ts +11 -3
- package/src/profiles-deepseek.ts +282 -0
- package/src/profiles.ts +71 -21
- package/src/resolve-dsh.ts +104 -8
- package/src/service.ts +64 -13
package/src/service.ts
CHANGED
|
@@ -18,10 +18,12 @@ import { deepEqualJson, installSettingsSection } from '@deepseek-ai/dsh-settings
|
|
|
18
18
|
|
|
19
19
|
import { hasBuiltinProvider } from './catalog/builtin.ts'
|
|
20
20
|
import { ModelsDevSource } from './catalog/models-dev.ts'
|
|
21
|
-
import { Config,
|
|
21
|
+
import { Config, type LlmPiConfig, SETTINGS_NS } from './config.ts'
|
|
22
|
+
import { DeepseekRouteRegistrar } from './deepseek-routes.ts'
|
|
22
23
|
import { discoverModels } from './discovery.ts'
|
|
23
24
|
import { assertServiceable, buildProfiles } from './profiles.ts'
|
|
24
|
-
import {
|
|
25
|
+
import { buildDeepseekRoutes, type ResolvedDeepseekRoute } from './profiles-deepseek.ts'
|
|
26
|
+
import { type DshKit, resolveDshKit } from './resolve-dsh.ts'
|
|
25
27
|
|
|
26
28
|
export interface LlmPiRuntime {
|
|
27
29
|
/** 当前生效配置(settings 用户层解析结果或 cordis 行级 config)。 */
|
|
@@ -46,7 +48,10 @@ function registrationFacts(profiles: ReadonlyMap<string, ResolvedPiAiProviderPro
|
|
|
46
48
|
|
|
47
49
|
/** 凭据解析(逐行对齐官方 resolveApiKey):凭据服务优先,缺失时启动环境兜底。 */
|
|
48
50
|
function makeResolveApiKey(ctx: Context, kit: DshKit) {
|
|
49
|
-
return async (
|
|
51
|
+
return async (
|
|
52
|
+
provider: string,
|
|
53
|
+
profile: ResolvedPiAiProviderProfile,
|
|
54
|
+
): Promise<string | undefined> => {
|
|
50
55
|
const ref = profile.apiKeyEnv
|
|
51
56
|
if (ref === undefined) return undefined
|
|
52
57
|
const credentials = ctx.get('credentials')
|
|
@@ -54,7 +59,8 @@ function makeResolveApiKey(ctx: Context, kit: DshKit) {
|
|
|
54
59
|
credentials !== undefined
|
|
55
60
|
? (await credentials.resolve(ref as CredentialRef))?.value
|
|
56
61
|
: launchEnvironmentOf(ctx).get(ref as unknown as string)?.value
|
|
57
|
-
if (hit !== undefined && hit.length > 0)
|
|
62
|
+
if (hit !== undefined && hit.length > 0)
|
|
63
|
+
return kit.assertUsableApiKey(hit, 'llm-pi', ref as unknown as string)
|
|
58
64
|
throw new kit.LlmError(
|
|
59
65
|
`llm-pi: provider route "${provider}" 的凭据引用 ${String(ref)} 未解析到值——` +
|
|
60
66
|
'请经凭据服务(web Models 页)存放或导出环境变量;仅当该 provider 应使用 pi-ai 自有环境发现时才移除 apiKeyEnv',
|
|
@@ -85,6 +91,7 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
85
91
|
let current: () => LlmPiConfig = () => config
|
|
86
92
|
let lastRaw: LlmPiConfig | undefined
|
|
87
93
|
let memoized: Map<string, ResolvedPiAiProviderProfile> | undefined
|
|
94
|
+
let memoizedDeepseek: Map<string, ResolvedDeepseekRoute> | undefined
|
|
88
95
|
const deps = { kit, modelsDev }
|
|
89
96
|
/** 当前已解析 profiles,按原始 config identity 备忘(官方同款模式)。
|
|
90
97
|
* 运行期走 lenient:数据源漂移时降级/跳过并告警,而非抛错弄挂整个 route。 */
|
|
@@ -92,12 +99,28 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
92
99
|
const raw = current()
|
|
93
100
|
if (raw === lastRaw && memoized !== undefined) return memoized
|
|
94
101
|
const next = raw.enabled
|
|
95
|
-
? buildProfiles(raw.providers, {
|
|
102
|
+
? buildProfiles(raw.providers, {
|
|
103
|
+
...deps,
|
|
104
|
+
lenient: true,
|
|
105
|
+
warn: (message) => logger.warn(message),
|
|
106
|
+
})
|
|
107
|
+
: new Map()
|
|
108
|
+
memoizedDeepseek = raw.enabled
|
|
109
|
+
? buildDeepseekRoutes(raw.providers, {
|
|
110
|
+
kit,
|
|
111
|
+
lenient: true,
|
|
112
|
+
warn: (message) => logger.warn(message),
|
|
113
|
+
})
|
|
96
114
|
: new Map()
|
|
97
115
|
lastRaw = raw
|
|
98
116
|
memoized = next
|
|
99
117
|
return next
|
|
100
118
|
}
|
|
119
|
+
/** deepseek 路由物化表(与 profiles 同一次备忘窗口)。 */
|
|
120
|
+
const deepseekRoutes = (): Map<string, ResolvedDeepseekRoute> => {
|
|
121
|
+
profiles()
|
|
122
|
+
return memoizedDeepseek ?? new Map()
|
|
123
|
+
}
|
|
101
124
|
profiles() // 行级 config 不可服务则启动即失败(官方同款 fail-fast)
|
|
102
125
|
|
|
103
126
|
const adapter = new kit.PiAiAdapter({
|
|
@@ -132,7 +155,10 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
132
155
|
let registrations: RegistrationGroup[] | undefined
|
|
133
156
|
let registeredFacts: unknown
|
|
134
157
|
|
|
135
|
-
const registerGroup = (
|
|
158
|
+
const registerGroup = (
|
|
159
|
+
routes: string[],
|
|
160
|
+
fallback: (error: unknown) => void,
|
|
161
|
+
): RegistrationGroup[] => {
|
|
136
162
|
try {
|
|
137
163
|
const handle = ctx.llm.registerAdapter(routes, adapter as never)
|
|
138
164
|
return [{ routes, handle }]
|
|
@@ -144,7 +170,9 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
144
170
|
const handle = ctx.llm.registerAdapter([route], adapter as never)
|
|
145
171
|
groups.push({ routes: [route], handle })
|
|
146
172
|
} catch (routeError) {
|
|
147
|
-
logger.error(
|
|
173
|
+
logger.error(
|
|
174
|
+
`llm-pi: route "${route}" 注册失败(可能与其他 adapter 重名),该 route 不可用`,
|
|
175
|
+
)
|
|
148
176
|
logger.error(routeError)
|
|
149
177
|
}
|
|
150
178
|
}
|
|
@@ -168,9 +196,11 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
168
196
|
})
|
|
169
197
|
} else {
|
|
170
198
|
try {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
199
|
+
const [first, ...rest] = registrations
|
|
200
|
+
if (first === undefined) throw new Error('registrations 为空(此前整批注册全部失败)')
|
|
201
|
+
first.handle.replace(routes)
|
|
202
|
+
for (const group of rest) group.handle.replace([])
|
|
203
|
+
registrations = [{ routes, handle: first.handle }]
|
|
174
204
|
} catch (error) {
|
|
175
205
|
// 原子 replace 被拒(含冲突):保留此前注册(官方同款护栏)
|
|
176
206
|
logger.error('llm-pi: 更新被拒,保留此前注册的 route')
|
|
@@ -183,13 +213,21 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
183
213
|
let directory: { replace: (entries: unknown[]) => void } | undefined
|
|
184
214
|
let directoryFacts: unknown
|
|
185
215
|
const ensureDirectory = (): void => {
|
|
186
|
-
const
|
|
216
|
+
const piEntries = [...profiles().entries()].map(([provider, profile]) => ({
|
|
187
217
|
provider,
|
|
188
218
|
displayName: profile.displayName,
|
|
189
219
|
settingsNs: SETTINGS_NS,
|
|
190
220
|
settingsPath: ['providers', provider],
|
|
191
221
|
declared: !hasBuiltinProvider(kit, provider),
|
|
192
222
|
}))
|
|
223
|
+
const deepseekEntries = [...deepseekRoutes().values()].map((built) => ({
|
|
224
|
+
provider: built.route,
|
|
225
|
+
displayName: built.displayName,
|
|
226
|
+
settingsNs: SETTINGS_NS,
|
|
227
|
+
settingsPath: ['providers', built.route],
|
|
228
|
+
declared: true,
|
|
229
|
+
}))
|
|
230
|
+
const entries = [...piEntries, ...deepseekEntries]
|
|
193
231
|
if (deepEqualJson(entries, directoryFacts)) return
|
|
194
232
|
if (entries.length === 0) {
|
|
195
233
|
// 空目录不可注册(INVALID_DIRECTORY);等 settings 用户层供数后在 onChange 注册
|
|
@@ -201,14 +239,21 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
201
239
|
directoryFacts = entries
|
|
202
240
|
}
|
|
203
241
|
|
|
242
|
+
const deepseekRegistrar = new DeepseekRouteRegistrar({
|
|
243
|
+
ctx,
|
|
244
|
+
kit,
|
|
245
|
+
logger: { warn: (m) => logger.warn(m), error: (m) => logger.error(m) },
|
|
246
|
+
routes: deepseekRoutes,
|
|
247
|
+
})
|
|
248
|
+
const ensureDeepseek = (): void => deepseekRegistrar.sync(deepseekRoutes())
|
|
249
|
+
|
|
204
250
|
ensureRegistration()
|
|
251
|
+
ensureDeepseek()
|
|
205
252
|
ensureDirectory()
|
|
206
253
|
|
|
207
|
-
let settingsBound = false
|
|
208
254
|
installSettingsSection(ctx, SETTINGS_NS, Config, config, {
|
|
209
255
|
validate: (cfg: LlmPiConfig) => assertServiceable(cfg, deps),
|
|
210
256
|
setSource: (source: () => LlmPiConfig) => {
|
|
211
|
-
settingsBound = true
|
|
212
257
|
current = source
|
|
213
258
|
},
|
|
214
259
|
onChange: () => {
|
|
@@ -218,6 +263,12 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
|
|
|
218
263
|
logger.error('llm-pi: 更新被拒,保留此前注册的 route')
|
|
219
264
|
logger.error(error)
|
|
220
265
|
}
|
|
266
|
+
try {
|
|
267
|
+
ensureDeepseek()
|
|
268
|
+
} catch (error) {
|
|
269
|
+
logger.error('llm-pi: deepseek route 更新失败,保留此前注册')
|
|
270
|
+
logger.error(error)
|
|
271
|
+
}
|
|
221
272
|
try {
|
|
222
273
|
ensureDirectory()
|
|
223
274
|
} catch (error) {
|