56daf6be49
Hospital records and adopted rectification times were still fed to the model as not_auto_rectified because the chart request omitted declared_accuracy/time_source and mastra hardcoded the boundary. Map profile truth into the engine request, keep rectified for accepted/confirmed active times only, and leave window/general guards unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
288 lines
11 KiB
TypeScript
288 lines
11 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
import { prepareConsultationRoute } from "../src/lib/consultation-route-service.ts";
|
|
import {
|
|
createBirthTimeModeOutputGuard,
|
|
shouldRunBirthChartWorkflow,
|
|
shouldRunDeclaredWindowWorkflow,
|
|
UNVERIFIED_BIRTH_TIME_NOTICE,
|
|
applyBirthTimeModeToWorkflowContext,
|
|
unverifiedBirthTimeNotice,
|
|
} from "../src/lib/consultation-birth-time-mode.ts";
|
|
import {
|
|
consultationInputSchema,
|
|
runConsultationWorkflow,
|
|
toAgentConsultationContext,
|
|
} from "../src/mastra/consultation-workflow.ts";
|
|
|
|
const profile = Object.freeze({
|
|
name: "岳辰",
|
|
birth_date: "1997-08-08",
|
|
reported_birth_time: "05:30:00",
|
|
active_birth_time: "05:18:00",
|
|
birth_time_source: "approximate",
|
|
birth_time_status: "candidate",
|
|
country_code: "CN",
|
|
province_code: "130000",
|
|
city_code: "130400",
|
|
district_code: "130406",
|
|
latitude: 36.420487,
|
|
longitude: 114.209936,
|
|
timezone_offset: 8,
|
|
});
|
|
|
|
const RANGE_INTERACTION = /按时间范围|candidate windows|not_auto_rectified/;
|
|
|
|
function agentContext(rectification: Record<string, unknown>) {
|
|
return toAgentConsultationContext({
|
|
success: true,
|
|
question: "事业如何",
|
|
chart: { modules: {} },
|
|
routing: { primary_theme: "career" },
|
|
consumer_context: {
|
|
route: "career",
|
|
core_status: "ready",
|
|
available_layers: ["D1", "D9", "D10"],
|
|
missing_route_layers: [],
|
|
hard_blockers: [],
|
|
answer_policy: {
|
|
can_answer_direction: true,
|
|
can_answer_precise_timing: true,
|
|
},
|
|
},
|
|
rectification,
|
|
});
|
|
}
|
|
|
|
async function chartRoute(
|
|
mode: "verified_chart" | "unverified_birth_time",
|
|
overrides: Record<string, unknown>,
|
|
) {
|
|
return prepareConsultationRoute({
|
|
userId: "user-1",
|
|
mode,
|
|
loadProfile: async () => ({ ...profile, ...overrides }),
|
|
reserve: async () => "reserved",
|
|
});
|
|
}
|
|
|
|
test("verified_chart users send rectified accuracy and do not consume a not_auto_rectified boundary", async () => {
|
|
const prepared = await chartRoute("verified_chart", {
|
|
birth_time_status: "accepted",
|
|
birth_time_source: "approximate",
|
|
});
|
|
const toolInput = prepared.serverChart?.toolInput;
|
|
assert.equal(toolInput?.declared_accuracy, "rectified");
|
|
|
|
const parsed = consultationInputSchema.parse({
|
|
...toolInput,
|
|
question: "事业如何",
|
|
theme: "career",
|
|
entryMode: "direct_chart",
|
|
});
|
|
assert.equal(parsed.declared_accuracy, "rectified");
|
|
|
|
let requestBody = "";
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = async (_input, init) => {
|
|
requestBody = typeof init?.body === "string" ? init.body : "";
|
|
return new Response(JSON.stringify({
|
|
success: true,
|
|
chart: {},
|
|
routing: {},
|
|
consumer_context: {
|
|
route: "career",
|
|
core_status: "ready",
|
|
available_layers: [],
|
|
missing_route_layers: [],
|
|
hard_blockers: [],
|
|
answer_policy: { can_answer_direction: true, can_answer_precise_timing: true },
|
|
},
|
|
}), { status: 200 });
|
|
};
|
|
try {
|
|
await runConsultationWorkflow(parsed);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
assert.equal(JSON.parse(requestBody).declared_accuracy, "rectified");
|
|
|
|
const context = agentContext({
|
|
effective_accuracy: "rectified",
|
|
summary: { headline: "出生时间风险较低,可进入完整解盘" },
|
|
enabled_vargas: { D1: "enabled", D9: "enabled", D10: "enabled" },
|
|
lagna_boundary: { is_sensitive: false },
|
|
});
|
|
assert.notEqual(context.rectification.boundary, "not_auto_rectified");
|
|
assert.doesNotMatch(JSON.stringify(context), RANGE_INTERACTION);
|
|
});
|
|
|
|
test("hospital_record users send minute/hospital and never receive rectified", async () => {
|
|
const prepared = await chartRoute("unverified_birth_time", {
|
|
birth_time_source: "hospital_record",
|
|
birth_time_status: "reported",
|
|
active_birth_time: "05:18:00",
|
|
});
|
|
const toolInput = prepared.serverChart?.toolInput;
|
|
assert.equal(toolInput?.declared_accuracy, "minute");
|
|
assert.equal(toolInput?.time_source, "hospital");
|
|
assert.notEqual(toolInput?.declared_accuracy, "rectified");
|
|
|
|
const mapper = await import("../src/lib/consultation-route-service.ts");
|
|
assert.equal(typeof mapper.declaredBirthAccuracyFromProfile, "function");
|
|
assert.deepEqual(mapper.declaredBirthAccuracyFromProfile({
|
|
birthTimeStatus: "reported",
|
|
birthTimeSource: "hospital_record",
|
|
hasActiveBirthTime: true,
|
|
}), { declaredAccuracy: "minute", timeSource: "hospital" });
|
|
});
|
|
|
|
test("family_exact and approximate keep the documented downgrade without range-interaction copy", async () => {
|
|
const family = await chartRoute("unverified_birth_time", {
|
|
birth_time_source: "family_exact",
|
|
birth_time_status: "reported",
|
|
active_birth_time: null,
|
|
});
|
|
assert.equal(family.serverChart?.toolInput.declared_accuracy, "minute");
|
|
assert.equal(family.serverChart?.toolInput.time_source, "family_clear");
|
|
assert.notEqual(family.serverChart?.toolInput.declared_accuracy, "rectified");
|
|
|
|
const approximate = await chartRoute("unverified_birth_time", {
|
|
birth_time_source: "approximate",
|
|
birth_time_status: "reported",
|
|
active_birth_time: null,
|
|
});
|
|
assert.equal(approximate.serverChart?.toolInput.declared_accuracy, "15min");
|
|
assert.notEqual(approximate.serverChart?.toolInput.declared_accuracy, "rectified");
|
|
|
|
const familyContext = agentContext({
|
|
effective_accuracy: "5min",
|
|
summary: { headline: "按填报时间排盘,分钟敏感结论已标注精度" },
|
|
enabled_vargas: { D1: "enabled", D9: "enabled", D10: "enabled" },
|
|
lagna_boundary: { is_sensitive: false },
|
|
});
|
|
assert.notEqual(familyContext.rectification.boundary, "not_auto_rectified");
|
|
assert.doesNotMatch(JSON.stringify(familyContext), RANGE_INTERACTION);
|
|
|
|
const approximateContext = agentContext({
|
|
effective_accuracy: "15min",
|
|
summary: { headline: "按填报时间排盘,分钟敏感结论已标注精度" },
|
|
enabled_vargas: { D1: "enabled", D9: "enabled", D10: "enabled_with_warning" },
|
|
lagna_boundary: { is_sensitive: false },
|
|
});
|
|
assert.notEqual(approximateContext.rectification.boundary, "not_auto_rectified");
|
|
assert.doesNotMatch(JSON.stringify(approximateContext), RANGE_INTERACTION);
|
|
});
|
|
|
|
test("rectified accuracy is reserved for accepted or confirmed profiles with an active birth time", async () => {
|
|
const mapper = await import("../src/lib/consultation-route-service.ts");
|
|
const map = mapper.declaredBirthAccuracyFromProfile;
|
|
assert.equal(typeof map, "function");
|
|
|
|
assert.deepEqual(map({
|
|
birthTimeStatus: "accepted",
|
|
birthTimeSource: "approximate",
|
|
hasActiveBirthTime: true,
|
|
}), { declaredAccuracy: "rectified", timeSource: "rectified" });
|
|
assert.deepEqual(map({
|
|
birthTimeStatus: "confirmed",
|
|
birthTimeSource: "hospital_record",
|
|
hasActiveBirthTime: true,
|
|
}), { declaredAccuracy: "rectified", timeSource: "rectified" });
|
|
assert.notEqual(map({
|
|
birthTimeStatus: "accepted",
|
|
birthTimeSource: "hospital_record",
|
|
hasActiveBirthTime: false,
|
|
}).declaredAccuracy, "rectified");
|
|
assert.notEqual(map({
|
|
birthTimeStatus: "candidate",
|
|
birthTimeSource: "hospital_record",
|
|
hasActiveBirthTime: true,
|
|
}).declaredAccuracy, "rectified");
|
|
assert.notEqual(map({
|
|
birthTimeStatus: "reported",
|
|
birthTimeSource: "family_exact",
|
|
hasActiveBirthTime: true,
|
|
}).declaredAccuracy, "rectified");
|
|
});
|
|
|
|
test("declared_birth_window and general_no_birth_time never enter the natal accuracy mapper", async () => {
|
|
const windowed = await prepareConsultationRoute({
|
|
userId: "user-1",
|
|
mode: "declared_birth_window",
|
|
loadProfile: async () => ({
|
|
...profile,
|
|
reported_birth_time: null,
|
|
active_birth_time: null,
|
|
birth_time_source: "period_only",
|
|
birth_time_period: "evening",
|
|
birth_time_status: "reported",
|
|
}),
|
|
reserve: async () => "reserved",
|
|
});
|
|
assert.equal(windowed.consultationMode, "declared_birth_window");
|
|
assert.equal(windowed.serverChart, null);
|
|
assert.equal("declared_accuracy" in (windowed.declaredWindow?.toolInput ?? {}), false);
|
|
assert.equal("time_source" in (windowed.declaredWindow?.toolInput ?? {}), false);
|
|
assert.equal(shouldRunBirthChartWorkflow("declared_birth_window"), false);
|
|
assert.equal(shouldRunDeclaredWindowWorkflow("declared_birth_window"), true);
|
|
assert.equal(shouldRunBirthChartWorkflow("general_no_birth_time"), false);
|
|
assert.equal(shouldRunDeclaredWindowWorkflow("general_no_birth_time"), false);
|
|
});
|
|
|
|
test("unverified notices grade by source without changing the output guards", () => {
|
|
assert.equal(unverifiedBirthTimeNotice("hospital_record"), "使用你填报的出生时间(医院记录)排盘");
|
|
assert.equal(unverifiedBirthTimeNotice("family_exact"), "使用你填报的出生时间排盘;分钟敏感结论的置信度已降低。");
|
|
assert.equal(unverifiedBirthTimeNotice("approximate"), UNVERIFIED_BIRTH_TIME_NOTICE);
|
|
assert.equal(unverifiedBirthTimeNotice("unknown"), UNVERIFIED_BIRTH_TIME_NOTICE);
|
|
|
|
const guarded = applyBirthTimeModeToWorkflowContext({
|
|
success: true,
|
|
consumer_context: {
|
|
core_status: "ready",
|
|
answer_policy: { can_answer_direction: true, can_answer_precise_timing: true },
|
|
},
|
|
}, "unverified_birth_time", { birthTimeSource: "hospital_record" });
|
|
assert.equal(
|
|
(guarded.consumer_context as { birth_time_notice?: string }).birth_time_notice,
|
|
"使用你填报的出生时间(医院记录)排盘",
|
|
);
|
|
assert.equal(guarded.consumer_context.answer_policy.can_answer_precise_timing, true);
|
|
});
|
|
|
|
test("output guards and window/general instruction seams stay byte-stable", () => {
|
|
const modeSource = readFileSync(new URL("../src/lib/consultation-birth-time-mode.ts", import.meta.url), "utf8");
|
|
const routeSource = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
|
|
|
|
assert.match(modeSource, /if \(mode === "general_no_birth_time"\) return guardGeneralNoBirthTimeOutput\(text\)/);
|
|
assert.match(
|
|
modeSource,
|
|
/if \(mode === "declared_birth_window" \|\| !canAnswerPreciseTiming\) \{[\s\S]*return guardPreciseTimingOutput\(text\);/,
|
|
);
|
|
assert.equal(UNVERIFIED_BIRTH_TIME_NOTICE.includes("未校正填报时间"), true);
|
|
|
|
const general = createBirthTimeModeOutputGuard("general_no_birth_time", false)(
|
|
"D9 在印度占星中通常用于观察婚姻与法则层面的成熟。\n你的上升是巨蟹座。",
|
|
);
|
|
assert.match(general, /D9 在印度占星中通常用于观察婚姻与法则层面的成熟/);
|
|
assert.doesNotMatch(general, /你的上升是巨蟹座/);
|
|
|
|
const windowed = createBirthTimeModeOutputGuard("declared_birth_window", false)(
|
|
"Rahu 大运为 2013年11月21日 至 2031年11月22日。",
|
|
);
|
|
assert.match(windowed, /具体时间已省略/);
|
|
|
|
assert.match(routeSource, /function generalNoMinuteInstruction\(hasPublicDaily: boolean\) \{/);
|
|
assert.match(routeSource, /function declaredWindowInstruction\(\) \{/);
|
|
assert.match(
|
|
routeSource,
|
|
/当前没有具体出生分钟。不得计算或推断个人星盘,也不得补 00:00、时段中点或任何候选分钟。/,
|
|
);
|
|
assert.match(
|
|
routeSource,
|
|
/当前是声明出生窗口咨询,没有单一出生分钟。如需个人结构结论,必须调用 run-jyotish-window-consultation。/,
|
|
);
|
|
});
|