import assert from "node:assert/strict"; import test from "node:test"; import { agentGenerationSettings, AGENT_MAX_OUTPUT_TOKENS } from "../src/lib/agent-generation-settings.ts"; import { activityCompletedSteps, activityCompletedTrail, activityElapsedLabel, nextActivityView, } from "../src/lib/chat-message-view.ts"; import { chartCalculationProgressLabel, CONSULTATION_CHART_CALCULATION_LABEL, } from "../src/lib/consultation-activity-labels.ts"; import { RECTIFICATION_TOOL_PROGRESS_LABELS, activityTraceFromReceipt, rectificationCompletedTrail, rectificationToolActivityPhase, } from "../src/lib/rectification-activity-labels.ts"; test("generation settings reserve spoken-answer tokens and disable thinking by default", () => { const settings = agentGenerationSettings({ providerId: "deepseek" }); assert.equal(AGENT_MAX_OUTPUT_TOKENS, 16_384); assert.equal(settings.modelSettings.maxOutputTokens, 16_384); assert.deepEqual(settings.providerOptions.openai, { thinking: { type: "disabled" } }); assert.deepEqual(settings.providerOptions.deepseek, { thinking: { type: "disabled" } }); }); test("callers can enable a separate thinking channel without pinching the answer budget", () => { const settings = agentGenerationSettings({ providerId: "deepseek" }, { thinking: "enabled" }); assert.equal(settings.modelSettings.maxOutputTokens, 16_384 + 8_192); assert.deepEqual(settings.providerOptions.openai, { thinking: { type: "enabled" } }); assert.deepEqual(settings.providerOptions.deepseek, { thinking: { type: "enabled" } }); }); test("activity elapsed copy stays hidden until eight seconds", () => { assert.equal(activityElapsedLabel(1_000, 8_999), null); assert.equal(activityElapsedLabel(1_000, 9_000), "已用时 8 秒"); assert.equal(activityElapsedLabel(1_000, 25_000), "已用时 24 秒"); }); test("the same activity label keeps its start time", () => { const first = nextActivityView(undefined, { phase: "chart-calculation", label: "正在比较候选时间…", }, 10); const same = nextActivityView(first, { phase: "chart-calculation", label: "正在比较候选时间…", }, 40); const next = nextActivityView(same, { phase: "answer-composition", label: "正在组织回答…", }, 50); assert.equal(first.startedAt, 10); assert.equal(same.startedAt, 10); assert.equal(next.startedAt, 50); }); test("completed-step trail stays short and drops while composing", () => { assert.equal(activityCompletedTrail([]), undefined); assert.equal( activityCompletedTrail(["读取校正记录", "整理事件证据"]), "已完成:读取校正记录 · 整理事件证据", ); assert.equal( activityCompletedTrail(["一", "二", "三", "四"]), "已完成:二 · 三 · 四", ); const waiting = nextActivityView(undefined, { phase: "chart-calculation", label: "正在比较候选时间…", completedTrail: activityCompletedTrail(["读取校正记录", "整理事件证据"]), }, 10); assert.equal(waiting.completedTrail, "已完成:读取校正记录 · 整理事件证据"); const composing = nextActivityView(waiting, { phase: "answer-composition", label: "正在组织回答…", }, 20); assert.equal(composing.completedTrail, undefined); assert.deepEqual( activityCompletedSteps("已完成:读取分析方法 · 计算本命盘"), ["读取分析方法", "计算本命盘"], ); }); test("live rectification labels name the actual public tool", () => { // 原值: "正在读取校正记录…" / "正在比较候选时间…" // 新值: "正在看你的资料…" / "正在重新对照盘面…" // 原因: BUG-1050 步骤名改大白话;对照盘面一句与 BUG-1047 已批准的阶段句同文 assert.equal(RECTIFICATION_TOOL_PROGRESS_LABELS["rectification-read-case"], "正在看你的资料…"); assert.equal(RECTIFICATION_TOOL_PROGRESS_LABELS["rectification-compare-candidates"], "正在重新对照盘面…"); assert.equal(rectificationToolActivityPhase("rectification-read-case"), "loading-method"); assert.equal(rectificationToolActivityPhase("rectification-compare-candidates"), "chart-calculation"); assert.equal(rectificationToolActivityPhase("rectification-propose-evidence"), "evidence-validation"); assert.equal( rectificationCompletedTrail(["rectification-read-case", "rectification-propose-evidence"]), // 原值: "已完成:读取校正记录 · 整理事件证据" / 新值: 大白话步骤名 / 原因: BUG-1050 "已完成:看了你的资料 · 记下你说的事", ); // BUG-1050: two tools that share one plain label are named once in the trail. assert.equal( rectificationCompletedTrail([ "rectification-read-case", "rectification-record-evidence-batch", "rectification-propose-evidence", ]), "已完成:看了你的资料 · 记下你说的事", ); }); test("persisted receipts rebuild the public activity steps without thinking text", () => { assert.deepEqual( activityTraceFromReceipt({ steps: ["rectification-read-case", "rectification-record-evidence-batch"], methods: ["d1-rashi", "d10-dashamsa"], }).map((row) => `${row.kind}:${row.label}`), // 原值: ["activity:读取校正记录", "activity:整理多条事件证据"] / 新值: 大白话步骤名 / 原因: BUG-1050 ["activity:看了你的资料", "activity:记下你说的事"], ); assert.deepEqual(activityTraceFromReceipt({ steps: [], methods: ["d1-rashi"] }), []); }); test("multi-domain chart calculation names the current item without domain ids", () => { assert.equal(chartCalculationProgressLabel(1, 1), CONSULTATION_CHART_CALCULATION_LABEL); assert.equal(chartCalculationProgressLabel(2, 3), "正在计算本命盘(第 2/3 项)…"); assert.doesNotMatch(chartCalculationProgressLabel(2, 3), /career|wealth|timing|marriage/); });