@zhushanwen/pi-rename-session 0.9.5 → 0.10.0

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/README.md CHANGED
@@ -131,7 +131,7 @@ node e2e/run-all.mjs # 顺序全跑:单场景失败不阻断后续,汇总
131
131
  - harness API 与断言纯函数(单测 `e2e/harness.test.mjs` 随 vitest 跑):`e2e/harness.mjs`
132
132
  - A2 的标题记录与人工抽查表(词组形态/语义相关/语言跟随三列):`e2e/RESULTS.md`(run-a2 自动追加,人工填写)
133
133
  - 保留现场调试:`E2E_KEEP_TMP=1 node e2e/run-all.mjs`
134
- - 测试模型固定 `xiaomi-token-plan-cn/mimo-v2.5-pro`(项目规范,禁 kimi)
134
+ - 测试模型缺省 `xiaomi-token-plan-cn/mimo-v2.6-flash`(`E2E_MODEL` 环境变量可覆盖,禁 kimi)
135
135
 
136
136
  ## 子 session 自动排除
137
137
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-rename-session",
3
- "version": "0.9.5",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "taiji": {
@@ -33,9 +33,9 @@
33
33
  "pi-package"
34
34
  ],
35
35
  "dependencies": {
36
- "@zhushanwen/pi-ext-guards": "0.4.1",
36
+ "@zhushanwen/pi-ext-guards": "0.4.2",
37
37
  "@zhushanwen/pi-extension-logger": "0.6.1",
38
- "@zhushanwen/pi-llm-shared": "0.10.0"
38
+ "@zhushanwen/pi-llm-shared": "0.11.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@vitest/coverage-v8": "^4.1.9",
@@ -380,7 +380,9 @@ const STUB_MODEL: Model<Api> = {
380
380
  api: "anthropic-messages",
381
381
  provider: "stub",
382
382
  baseUrl: "https://stub.invalid",
383
- reasoning: false,
383
+ // reasoning=true:档位按模型 supportedLevels 数据驱动判定(pi-ai getSupportedThinkingLevels),
384
+ // 不支持思考的模型只支持 "off"——用例要断言档位透传,故此处声明支持。
385
+ reasoning: true,
384
386
  input: ["text"],
385
387
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
386
388
  contextWindow: 128_000,
@@ -672,6 +674,21 @@ describe("callRenameLLM", () => {
672
674
  const callOpts = vi.mocked(callLLM).mock.calls[0][1] as { reasoning?: unknown };
673
675
  expect(callOpts.reasoning).toBe("high");
674
676
  });
677
+
678
+ it("thinkingLevel 为该模型不支持的档位 → warn 留痕 + 不传 reasoning(数据驱动判定,不静默换档)", async () => {
679
+ loggerMock.warn.mockClear();
680
+ vi.mocked(resolveModel).mockReturnValue(STUB_MODEL);
681
+ vi.mocked(callLLM).mockResolvedValue({ ok: true, content: "标题" });
682
+ // STUB_MODEL(reasoning=true)的 supportedLevels = off/minimal/low/medium/high,
683
+ // "ultra" 词表外 → reasoningLevel undefined → 不传档位
684
+ await callRenameLLM(createCtx(), { ...BASE_CONFIG, thinkingLevel: "ultra" }, FINAL_MESSAGE);
685
+
686
+ const callOpts = vi.mocked(callLLM).mock.calls[0][1] as { reasoning?: unknown };
687
+ expect(callOpts.reasoning).toBeUndefined();
688
+ expect(loggerMock.warn).toHaveBeenCalledWith(
689
+ expect.stringContaining('thinkingLevel "ultra" is not supported'),
690
+ );
691
+ });
675
692
  });
676
693
 
677
694
  // ────────────────────────────────────────────────────
@@ -301,8 +301,10 @@ describe("normalizeRenameConfig", () => {
301
301
  expect(normalizeRenameConfig({ thinkingLevel: "off" }).thinkingLevel).toBe("off");
302
302
  });
303
303
 
304
- it("thinkingLevel 非法(未知值 / 非字符串)→ 回默认 off", () => {
305
- expect(normalizeRenameConfig({ thinkingLevel: "ultra" }).thinkingLevel).toBe("off");
304
+ it("thinkingLevel:非空字符串原样采信(含词表外值),非字符串/缺失 → 默认 off", () => {
305
+ // 词表不在本层(合法性归写入侧 UI 与 pi):显式写了未知档位不再被静默换成缺省档
306
+ expect(normalizeRenameConfig({ thinkingLevel: "ultra" }).thinkingLevel).toBe("ultra");
307
+ expect(normalizeRenameConfig({ thinkingLevel: "xhigh" }).thinkingLevel).toBe("xhigh");
306
308
  expect(normalizeRenameConfig({ thinkingLevel: 5 }).thinkingLevel).toBe("off");
307
309
  expect(normalizeRenameConfig({ thinkingLevel: null }).thinkingLevel).toBe("off");
308
310
  // 缺失字段 → 默认 off(旧配置文件兼容:无 thinkingLevel 字段的存量配置自动回填)
package/src/llm.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
 
3
+ import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
3
4
  import type { AssistantMessage, Message, Usage } from "@earendil-works/pi-ai";
4
5
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
5
6
  import { isRecord } from "@zhushanwen/pi-ext-guards";
@@ -268,6 +269,12 @@ export async function callRenameLLM(
268
269
  logger.warn("model not available, skipping");
269
270
  return null;
270
271
  }
272
+ const reasoningLevel = getSupportedThinkingLevels(model).find((l) => l === config.thinkingLevel);
273
+ if (config.thinkingLevel !== undefined && reasoningLevel === undefined) {
274
+ logger.warn(
275
+ `configured thinkingLevel "${config.thinkingLevel}" is not supported by ${model.provider}/${model.id}; calling without a thinking level`,
276
+ );
277
+ }
271
278
 
272
279
  const userPrompt =
273
280
  options?.promptText ??
@@ -307,8 +314,10 @@ export async function callRenameLLM(
307
314
  maxTokens: 2048,
308
315
  // 固定 30s 超时(网络抖动归一为 ok:false 走静默跳过,不悬挂 fire-and-forget promise)
309
316
  timeoutMs: RENAME_TIMEOUT_MS,
310
- // thinkingLevel 直接透传(含 "off");llm-shared 内部会把 "off" 映射为不传 reasoning(provider 默认)
311
- reasoning: config.thinkingLevel,
317
+ // 档位按「该模型自己的 supportedLevels」判定(pi-ai 数据驱动,本层不自持词表):
318
+ // 配置写了该模型不支持的档位 → 留痕并按「不传档位」处理(不静默换成别的档)。
319
+ // llm-shared 内部会把 "off" 映射为不传 reasoning(provider 默认)。
320
+ reasoning: reasoningLevel,
312
321
  // 保留随 session abort 取消调用的语义(旧版 llm.ts 同样透传 ctx.signal)
313
322
  signal: ctx.signal,
314
323
  sessionId,
package/src/pure.ts CHANGED
@@ -2,11 +2,10 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
 
4
4
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
- import type { ModelThinkingLevel } from "@earendil-works/pi-ai";
6
5
 
7
6
  import { isEnoentError } from "@zhushanwen/pi-ext-guards";
8
7
  import {
9
- isThinkingLevel,
8
+ normalizeThinkingLevel,
10
9
  loadConfig,
11
10
  normalizeModelSelector,
12
11
  saveConfig,
@@ -40,11 +39,11 @@ export interface RenameSessionConfig {
40
39
  /** 标题最大长度(Unicode 码点数)。 */
41
40
  maxTitleLength: number;
42
41
  /**
43
- * 标题生成 LLM 的 thinking 级别(pi 的 ModelThinkingLevel,THINKING_ORDER SSOT)。
42
+ * 标题生成 LLM 的 thinking 级别(字符串透传;合法档位按模型 supportedLevels 在调用点判定)。
44
43
  * 默认 "off":不传 pi-ai reasoning(provider 默认行为,与旧版本一致);
45
44
  * "minimal"~"max" 透传给 SimpleStreamOptions.reasoning(provider 不支持时静默忽略)。
46
45
  */
47
- thinkingLevel: ModelThinkingLevel;
46
+ thinkingLevel: string;
48
47
  }
49
48
 
50
49
  // ──────────────────────── 枚举校验 ────────────────────────
@@ -144,9 +143,7 @@ export function normalizeRenameConfig(raw: unknown): RenameSessionConfig {
144
143
  // normalizeModelSelector:llm-shared 导出(ext-simplify-17 D6 收口)
145
144
  const model = normalizeModelSelector(obj.model) ?? DEFAULT_RENAME_CONFIG.model;
146
145
 
147
- const thinkingLevel = isThinkingLevel(obj.thinkingLevel)
148
- ? obj.thinkingLevel
149
- : DEFAULT_RENAME_CONFIG.thinkingLevel;
146
+ const thinkingLevel = normalizeThinkingLevel(obj.thinkingLevel) ?? DEFAULT_RENAME_CONFIG.thinkingLevel;
150
147
 
151
148
  return { enabled, model, mode, maxTitleLength, thinkingLevel };
152
149
  }