@dsh-plus/llm-pi 0.1.8 → 0.1.9

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
@@ -508,7 +508,7 @@ var DeepseekRouteRegistrar = class {
508
508
  * @module llm-pi/directory
509
509
  */
510
510
  /** 组装当前全部 route 的目录条目(pi + deepseek 两族)。 */
511
- function buildDirectoryEntries(kit, settingsNs, piProfiles, deepseekRoutes) {
511
+ function buildDirectoryEntries(kit, settingsNs, piProfiles, deepseekRoutes, drafts) {
512
512
  const piEntries = [...piProfiles.entries()].map(([provider, profile]) => ({
513
513
  provider,
514
514
  displayName: profile.displayName,
@@ -523,7 +523,18 @@ function buildDirectoryEntries(kit, settingsNs, piProfiles, deepseekRoutes) {
523
523
  settingsPath: ["providers", built.route],
524
524
  declared: true
525
525
  }));
526
- return [...piEntries, ...deepseekEntries];
526
+ const draftEntries = drafts.map(({ route, displayName }) => ({
527
+ provider: route,
528
+ displayName,
529
+ settingsNs,
530
+ settingsPath: ["providers", route],
531
+ declared: true
532
+ }));
533
+ return [
534
+ ...piEntries,
535
+ ...deepseekEntries,
536
+ ...draftEntries
537
+ ];
527
538
  }
528
539
  /**
529
540
  * 提交目录条目;整批被原子拒绝时逐个剔除冲突条目重试(每轮至多剔一个,
@@ -1200,14 +1211,24 @@ function materializeRouteModels(route, profile, deps, defaultInput) {
1200
1211
  };
1201
1212
  }
1202
1213
  /**
1214
+ * 草稿路由:adapter pi、无 models 且无 provider 级 extends——占位待补
1215
+ * (用户先建路由、后填模型的工作流)。草稿不注册进 adapter(无模型可服务),
1216
+ * 但仍出现在可配置 provider 目录里(Models 页/配置卡片可见可编辑)。
1217
+ */
1218
+ function isDraftRoute(profile) {
1219
+ return (profile.adapter ?? "pi") === "pi" && (profile.models === void 0 || profile.models.length === 0) && profile.extends === void 0;
1220
+ }
1221
+ /**
1203
1222
  * 校验并物化全部 route。任一 route 不可服务即整体抛错——
1204
1223
  * settings 写入校验与运行期 profiles 回调共用本函数,
1205
1224
  * 保证"写入时被拒"与"运行期不可能拿到坏配置"互为表里。
1225
+ * 草稿路由(isDraftRoute)跳过物化,不参与 adapter 注册。
1206
1226
  */
1207
1227
  function buildProfiles(providers, deps) {
1208
1228
  const resolved = /* @__PURE__ */ new Map();
1209
1229
  for (const [route, profile] of Object.entries(providers ?? {})) {
1210
1230
  if ((profile.adapter ?? "pi") === "deepseek") continue;
1231
+ if (isDraftRoute(profile)) continue;
1211
1232
  if (route.length === 0) throw new Error("llm-pi: provider 名不能为空");
1212
1233
  if (profile.baseURL !== void 0 && profile.baseURL.length === 0) invalid(route, "baseURL 为空");
1213
1234
  if (profile.displayName !== void 0 && profile.displayName.length === 0) invalid(route, "displayName 为空");
@@ -1587,8 +1608,16 @@ async function startRuntime(ctx, rawConfig) {
1587
1608
  };
1588
1609
  let directory;
1589
1610
  let directoryFacts;
1611
+ /** 草稿路由(无模型占位)从原始配置直读——它们不进 adapter profiles。 */
1612
+ const draftRoutes = () => {
1613
+ const providers = current().providers ?? {};
1614
+ return Object.entries(providers).filter(([, profile]) => isDraftRoute(profile)).map(([route, profile]) => ({
1615
+ route,
1616
+ displayName: profile.displayName ?? route
1617
+ }));
1618
+ };
1590
1619
  const ensureDirectory = () => {
1591
- const entries = buildDirectoryEntries(kit, SETTINGS_NS, profiles(), deepseekRoutes());
1620
+ const entries = buildDirectoryEntries(kit, SETTINGS_NS, profiles(), deepseekRoutes(), draftRoutes());
1592
1621
  if (deepEqualJson(entries, directoryFacts)) return;
1593
1622
  if (entries.length === 0) {
1594
1623
  directoryFacts = entries;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/llm-pi",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "dsh-plus service+ui plugin: 基于 PiAiAdapter 的自定义 LLM 路由(全量 compat、模型继承、models.dev 目录兜底)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
package/src/directory.ts CHANGED
@@ -26,6 +26,7 @@ export function buildDirectoryEntries(
26
26
  settingsNs: string,
27
27
  piProfiles: Map<string, { displayName: string }>,
28
28
  deepseekRoutes: Map<string, ResolvedDeepseekRoute>,
29
+ drafts: ReadonlyArray<{ route: string; displayName: string }>,
29
30
  ): DirectoryEntry[] {
30
31
  const piEntries = [...piProfiles.entries()].map(([provider, profile]) => ({
31
32
  provider,
@@ -41,7 +42,15 @@ export function buildDirectoryEntries(
41
42
  settingsPath: ['providers', built.route],
42
43
  declared: true,
43
44
  }))
44
- return [...piEntries, ...deepseekEntries]
45
+ // 草稿路由不进 adapter,但要在目录里可见(Models 页/配置卡片入口)
46
+ const draftEntries = drafts.map(({ route, displayName }) => ({
47
+ provider: route,
48
+ displayName,
49
+ settingsNs,
50
+ settingsPath: ['providers', route],
51
+ declared: true,
52
+ }))
53
+ return [...piEntries, ...deepseekEntries, ...draftEntries]
45
54
  }
46
55
 
47
56
  /**
package/src/profiles.ts CHANGED
@@ -295,10 +295,24 @@ function materializeRouteModels(
295
295
  return { models, configuredMaxTokens }
296
296
  }
297
297
 
298
+ /**
299
+ * 草稿路由:adapter pi、无 models 且无 provider 级 extends——占位待补
300
+ * (用户先建路由、后填模型的工作流)。草稿不注册进 adapter(无模型可服务),
301
+ * 但仍出现在可配置 provider 目录里(Models 页/配置卡片可见可编辑)。
302
+ */
303
+ export function isDraftRoute(profile: ProviderProfileConfig): boolean {
304
+ return (
305
+ (profile.adapter ?? 'pi') === 'pi' &&
306
+ (profile.models === undefined || profile.models.length === 0) &&
307
+ profile.extends === undefined
308
+ )
309
+ }
310
+
298
311
  /**
299
312
  * 校验并物化全部 route。任一 route 不可服务即整体抛错——
300
313
  * settings 写入校验与运行期 profiles 回调共用本函数,
301
314
  * 保证"写入时被拒"与"运行期不可能拿到坏配置"互为表里。
315
+ * 草稿路由(isDraftRoute)跳过物化,不参与 adapter 注册。
302
316
  */
303
317
  export function buildProfiles(
304
318
  providers: Record<string, ProviderProfileConfig> | undefined,
@@ -308,6 +322,7 @@ export function buildProfiles(
308
322
  for (const [route, profile] of Object.entries(providers ?? {})) {
309
323
  // adapter: deepseek 的 route 由 profiles-deepseek.ts 物化(官方 DeepSeekAdapter 链路)
310
324
  if ((profile.adapter ?? 'pi') === 'deepseek') continue
325
+ if (isDraftRoute(profile)) continue // 草稿路由:占位待补,不进 adapter
311
326
  if (route.length === 0) throw new Error('llm-pi: provider 名不能为空')
312
327
  // 注意:不做"route 名与内置 provider 名重名"的静态校验——内置名 ≠ 已注册
313
328
  // route(如官方 llm-pi-ai 配置清空后 anthropic 名可用)。真实冲突只在注册期
package/src/service.ts CHANGED
@@ -21,7 +21,7 @@ import { Config, type LlmPiConfig, SETTINGS_NS } from './config.ts'
21
21
  import { DeepseekRouteRegistrar } from './deepseek-routes.ts'
22
22
  import { buildDirectoryEntries, commitDirectory, type DirectoryEntry } from './directory.ts'
23
23
  import { discoverModels } from './discovery.ts'
24
- import { assertServiceable, buildProfiles } from './profiles.ts'
24
+ import { assertServiceable, buildProfiles, isDraftRoute } from './profiles.ts'
25
25
  import { buildDeepseekRoutes, type ResolvedDeepseekRoute } from './profiles-deepseek.ts'
26
26
  import { type DshKit, resolveDshKit } from './resolve-dsh.ts'
27
27
 
@@ -212,8 +212,21 @@ export async function startRuntime(ctx: Context, rawConfig: LlmPiConfig): Promis
212
212
 
213
213
  let directory: { replace: (entries: unknown[]) => void } | undefined
214
214
  let directoryFacts: unknown
215
+ /** 草稿路由(无模型占位)从原始配置直读——它们不进 adapter profiles。 */
216
+ const draftRoutes = (): { route: string; displayName: string }[] => {
217
+ const providers = current().providers ?? {}
218
+ return Object.entries(providers)
219
+ .filter(([, profile]) => isDraftRoute(profile))
220
+ .map(([route, profile]) => ({ route, displayName: profile.displayName ?? route }))
221
+ }
215
222
  const ensureDirectory = (): void => {
216
- const entries = buildDirectoryEntries(kit, SETTINGS_NS, profiles(), deepseekRoutes())
223
+ const entries = buildDirectoryEntries(
224
+ kit,
225
+ SETTINGS_NS,
226
+ profiles(),
227
+ deepseekRoutes(),
228
+ draftRoutes(),
229
+ )
217
230
  // 备忘键为目标全集(含被冲突跳过的条目):目标不变不重复尝试/告警
218
231
  if (deepEqualJson(entries, directoryFacts)) return
219
232
  if (entries.length === 0) {