@dsh-plus/llm-pi 0.1.9 → 0.1.11

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/index.js CHANGED
@@ -276,6 +276,12 @@ function fetchJson(url, proxy, timeoutMs) {
276
276
  }, (response) => {
277
277
  const chunks = [];
278
278
  let size = 0;
279
+ let settled = false;
280
+ const fail = (error) => {
281
+ if (settled) return;
282
+ settled = true;
283
+ reject(error);
284
+ };
279
285
  response.on("data", (chunk) => {
280
286
  size += chunk.length;
281
287
  if (size > MAX_RESPONSE_BYTES$1) {
@@ -285,11 +291,14 @@ function fetchJson(url, proxy, timeoutMs) {
285
291
  chunks.push(chunk);
286
292
  });
287
293
  response.on("end", () => {
294
+ if (settled) return;
295
+ settled = true;
288
296
  resolve({
289
297
  status: response.statusCode ?? 0,
290
298
  body: Buffer.concat(chunks).toString("utf8")
291
299
  });
292
300
  });
301
+ response.on("error", fail);
293
302
  });
294
303
  req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`)));
295
304
  req.on("error", reject);
@@ -449,20 +458,23 @@ var DeepseekRouteRegistrar = class {
449
458
  for (const [route, built] of target) {
450
459
  const existing = this.registrations.get(route);
451
460
  if (existing !== void 0) {
452
- this.refreshPolicy(route, built, existing);
461
+ this.refreshRegistration(route, built, existing);
453
462
  continue;
454
463
  }
455
464
  this.register(route, built);
456
465
  }
457
466
  }
458
- refreshPolicy(route, built, registration) {
459
- const policy = built.connection.retryPolicy;
460
- if (deepEqualJson(policy, registration.retryPolicy)) return;
467
+ /** retryPolicy displayName 变化时原地 replace(两者均为注册期捕获事实)。 */
468
+ refreshRegistration(route, built, registration) {
469
+ const policyUnchanged = deepEqualJson(built.connection.retryPolicy, registration.retryPolicy);
470
+ const nameUnchanged = built.displayName === registration.displayName;
471
+ if (policyUnchanged && nameUnchanged) return;
461
472
  try {
462
473
  registration.handle.replace([route]);
463
- registration.retryPolicy = policy;
474
+ registration.retryPolicy = built.connection.retryPolicy;
475
+ registration.displayName = built.displayName;
464
476
  } catch (error) {
465
- this.deps.logger.error(`llm-pi: deepseek route "${route}" 重试策略更新被拒,保留此前注册`);
477
+ this.deps.logger.error(`llm-pi: deepseek route "${route}" 注册事实更新被拒,保留此前注册`);
466
478
  this.deps.logger.error(error);
467
479
  }
468
480
  }
@@ -484,11 +496,16 @@ var DeepseekRouteRegistrar = class {
484
496
  resolveUserId: () => this.resolveUserId(),
485
497
  resolveAttachments: () => ctx.get("attachments")
486
498
  });
499
+ adapter.providerInfo = (provider) => ({
500
+ id: provider,
501
+ name: routes().get(route)?.displayName ?? built.displayName
502
+ });
487
503
  try {
488
504
  const handle = ctx.llm.registerAdapter([route], adapter);
489
505
  this.registrations.set(route, {
490
506
  handle,
491
- retryPolicy: built.connection.retryPolicy
507
+ retryPolicy: built.connection.retryPolicy,
508
+ displayName: built.displayName
492
509
  });
493
510
  } catch (error) {
494
511
  logger.error(`llm-pi: deepseek route "${route}" 注册失败(可能与其他 adapter 重名),该 route 不可用`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/llm-pi",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "dsh-plus service+ui plugin: 基于 PiAiAdapter 的自定义 LLM 路由(全量 compat、模型继承、models.dev 目录兜底)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -82,6 +82,12 @@ function fetchJson(url: string, proxy: string, timeoutMs: number): Promise<JsonR
82
82
  (response) => {
83
83
  const chunks: Buffer[] = []
84
84
  let size = 0
85
+ let settled = false
86
+ const fail = (error: Error): void => {
87
+ if (settled) return
88
+ settled = true
89
+ reject(error)
90
+ }
85
91
  response.on('data', (chunk: Buffer) => {
86
92
  size += chunk.length
87
93
  if (size > MAX_RESPONSE_BYTES) {
@@ -91,11 +97,16 @@ function fetchJson(url: string, proxy: string, timeoutMs: number): Promise<JsonR
91
97
  chunks.push(chunk)
92
98
  })
93
99
  response.on('end', () => {
100
+ if (settled) return
101
+ settled = true
94
102
  resolve({
95
103
  status: response.statusCode ?? 0,
96
104
  body: Buffer.concat(chunks).toString('utf8'),
97
105
  })
98
106
  })
107
+ // 响应流自身出错(中途截断的 aborted 等)不经请求转发:缺此监听时
108
+ // Promise 永不 settle(req timeout 只覆盖建连/空闲等待),refresh 永久挂起。
109
+ response.on('error', fail)
99
110
  },
100
111
  )
101
112
  req.on('timeout', () => req.destroy(new Error(`请求超时(${timeoutMs}ms)`)))
@@ -21,7 +21,7 @@ import type { Context } from '@deepseek-ai/cordis'
21
21
  import { SETTINGS_NS } from '../ns.ts'
22
22
  import { LlmPiCard } from './card.tsx'
23
23
  import { en, NS, zh } from './i18n.ts'
24
- import type { Scope, SettingsApi } from './scope.ts'
24
+ import type { Scope, ScopeSnapshot, SettingsApi } from './scope.ts'
25
25
  import { injectStyle } from './styles.ts'
26
26
 
27
27
  export const name = 'dsh-plus-llm-pi'
@@ -35,8 +35,11 @@ export interface SettingsApi {
35
35
  result: {
36
36
  ok: boolean
37
37
  value?: {
38
+ writable: boolean
38
39
  namespaces: Array<{
39
40
  ns: string
41
+ value: unknown
42
+ revision: number
40
43
  secrets: Array<{ path: string[]; set: boolean }>
41
44
  }>
42
45
  }
@@ -25,6 +25,8 @@ interface RouteRegistration {
25
25
  handle: AdapterRegistrationHandle
26
26
  /** 注册期捕获的重试策略(变化才 replace)。 */
27
27
  retryPolicy: unknown
28
+ /** 注册期捕获的展示名(providerInfo 是注册期捕获事实,变化才 replace)。 */
29
+ displayName: string
28
30
  }
29
31
 
30
32
  export interface DeepseekRegistrarDeps {
@@ -80,25 +82,28 @@ export class DeepseekRouteRegistrar {
80
82
  for (const [route, built] of target) {
81
83
  const existing = this.registrations.get(route)
82
84
  if (existing !== undefined) {
83
- this.refreshPolicy(route, built, existing)
85
+ this.refreshRegistration(route, built, existing)
84
86
  continue
85
87
  }
86
88
  this.register(route, built)
87
89
  }
88
90
  }
89
91
 
90
- private refreshPolicy(
92
+ /** retryPolicy 或 displayName 变化时原地 replace(两者均为注册期捕获事实)。 */
93
+ private refreshRegistration(
91
94
  route: string,
92
95
  built: ResolvedDeepseekRoute,
93
96
  registration: RouteRegistration,
94
97
  ): void {
95
- const policy = built.connection.retryPolicy
96
- if (deepEqualJson(policy, registration.retryPolicy)) return
98
+ const policyUnchanged = deepEqualJson(built.connection.retryPolicy, registration.retryPolicy)
99
+ const nameUnchanged = built.displayName === registration.displayName
100
+ if (policyUnchanged && nameUnchanged) return
97
101
  try {
98
102
  registration.handle.replace([route])
99
- registration.retryPolicy = policy
103
+ registration.retryPolicy = built.connection.retryPolicy
104
+ registration.displayName = built.displayName
100
105
  } catch (error) {
101
- this.deps.logger.error(`llm-pi: deepseek route "${route}" 重试策略更新被拒,保留此前注册`)
106
+ this.deps.logger.error(`llm-pi: deepseek route "${route}" 注册事实更新被拒,保留此前注册`)
102
107
  this.deps.logger.error(error)
103
108
  }
104
109
  }
@@ -126,9 +131,20 @@ export class DeepseekRouteRegistrar {
126
131
  resolveUserId: () => this.resolveUserId() as never,
127
132
  resolveAttachments: () => ctx.get('attachments') as never,
128
133
  })
134
+ // 官方 providerInfo 硬编码 name: "DeepSeek"——覆盖为按路由动态读取的
135
+ // displayName,否则自定义 deepseek 路由在模型选择器里与 deepseek-official
136
+ // 撞名(两个 "DeepSeek" 分组,无法区分)。
137
+ adapter.providerInfo = (provider: string) => ({
138
+ id: provider,
139
+ name: routes().get(route)?.displayName ?? built.displayName,
140
+ })
129
141
  try {
130
142
  const handle = ctx.llm.registerAdapter([route], adapter as never)
131
- this.registrations.set(route, { handle, retryPolicy: built.connection.retryPolicy })
143
+ this.registrations.set(route, {
144
+ handle,
145
+ retryPolicy: built.connection.retryPolicy,
146
+ displayName: built.displayName,
147
+ })
132
148
  } catch (error) {
133
149
  logger.error(
134
150
  `llm-pi: deepseek route "${route}" 注册失败(可能与其他 adapter 重名),该 route 不可用`,