164 lines
5.8 KiB
TypeScript
164 lines
5.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
ACCEPTED_RANGE_READING_INSTRUCTION,
|
|
} from "../src/lib/consultation-birth-time-mode.ts";
|
|
import { applyPass4Policy } from "../src/lib/timing-output-guard.ts";
|
|
import { RANGE_READING_COPY } from "../src/lib/rectification-agentic/v9/adopt-narration.ts";
|
|
import {
|
|
createConsultationRuntimeState,
|
|
createWindowConsultationTools,
|
|
} from "../src/mastra/consultation-tools.ts";
|
|
import {
|
|
consultationWorkflowReceipt,
|
|
minuteSensitiveThemesFromBirthTimeSensitivity,
|
|
} from "../src/mastra/consultation-workflow.ts";
|
|
|
|
const toolContext = {
|
|
observe: { span: async (_name: string, fn: () => Promise<unknown>) => fn(), log() {} },
|
|
} as never;
|
|
|
|
test("theme_sensitivity maps sensitive keys onto the consultation answer policy", () => {
|
|
const themes = minuteSensitiveThemesFromBirthTimeSensitivity({
|
|
theme_sensitivity: {
|
|
career: { status: "stable" },
|
|
marriage: { status: "sensitive" },
|
|
wealth: { status: "stable" },
|
|
},
|
|
});
|
|
assert.deepEqual(themes, ["marriage"]);
|
|
|
|
const receipt = consultationWorkflowReceipt({
|
|
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,
|
|
minute_sensitive_themes: ["marriage"],
|
|
},
|
|
},
|
|
});
|
|
assert.deepEqual(receipt.minuteSensitiveThemes, ["marriage"]);
|
|
});
|
|
|
|
test("minute-sensitive or timing themes keep calculated dasha dates", () => {
|
|
// 原值:verified_chart + marriage/timing 主题把日期挖成「[具体时间已省略]」
|
|
// 新值:日期原样保留,Pass 4 只记 observe
|
|
// 原因:BUG-948,已校正用户问应期不该被删字;范围用户同样要拿到区间说法。
|
|
const text = "Rahu 大运为 2013年11月21日 至 2031年11月22日。";
|
|
const marriage = applyPass4Policy(text, "verified_chart");
|
|
assert.equal(marriage.text, text);
|
|
assert.equal(marriage.steps.some((step) => step.action === "observe" && step.kind === "exact-timing"), true);
|
|
|
|
const career = applyPass4Policy(text, "verified_chart");
|
|
assert.equal(career.text, text);
|
|
|
|
const timing = applyPass4Policy(text, "verified_chart");
|
|
assert.equal(timing.text, text);
|
|
assert.doesNotMatch(timing.text, /具体时间已省略/);
|
|
});
|
|
|
|
test("declared birth window keeps exact dates and does not blank them", () => {
|
|
const text = "Rahu 大运为 2013年11月21日 至 2031年11月22日。按范围看应期,不写成单一分钟。";
|
|
const report = applyPass4Policy(text, "declared_birth_window");
|
|
assert.equal(report.text, text);
|
|
assert.match(report.text, /2013年11月21日/);
|
|
assert.doesNotMatch(report.text, /具体时间已省略/);
|
|
assert.equal(report.steps.some((step) => step.action === "observe" && step.kind === "exact-timing"), true);
|
|
});
|
|
|
|
test("declared window consultation merges range-reading themes and says it is a coarse look", async () => {
|
|
const state = createConsultationRuntimeState();
|
|
const tools = createWindowConsultationTools({
|
|
userId: "u",
|
|
sessionId: "s",
|
|
requestId: "r-window",
|
|
consultationMode: "declared_birth_window",
|
|
declaredWindow: {
|
|
name: "测试",
|
|
toolInput: {
|
|
year: 1997,
|
|
month: 8,
|
|
day: 8,
|
|
city: "邯郸",
|
|
lat: 36.4,
|
|
lon: 114.2,
|
|
tz: 8,
|
|
ayanamsa: "raman",
|
|
rangeStart: "14:00",
|
|
rangeEnd: "18:00",
|
|
},
|
|
truth: {
|
|
birthDate: "1997-08-08",
|
|
birthTimeSource: "period_only",
|
|
birthTimePeriod: "afternoon",
|
|
birthTimeStatus: "reported",
|
|
wrapsMidnight: false,
|
|
placeLabel: "邯郸",
|
|
placeCodes: {
|
|
countryCode: "CN",
|
|
provinceCode: null,
|
|
cityCode: null,
|
|
districtCode: null,
|
|
},
|
|
placeId: null,
|
|
placeType: null,
|
|
placeProvider: null,
|
|
timezoneId: null,
|
|
timezoneSource: null,
|
|
latitude: 36.4,
|
|
longitude: 114.2,
|
|
timezoneOffset: 8,
|
|
},
|
|
},
|
|
state,
|
|
fetchWindowChart: async () => ({
|
|
declared_range: { start: "14:00", end: "18:00", wraps_midnight: false },
|
|
probe_count: 3,
|
|
probes: [
|
|
{ clock: "14:00", role: "range_start" as const },
|
|
{ clock: "16:00", role: "interior" as const },
|
|
{ clock: "18:00", role: "range_end" as const },
|
|
],
|
|
stable_layers: { planet_signs: { sun: "Cancer" } },
|
|
varying_layers: { ascendant_signs: ["Libra", "Scorpio"] },
|
|
blocked_layers: ["precise-timing"],
|
|
answer_policy: {
|
|
can_answer_direction: true,
|
|
can_answer_precise_timing: false as const,
|
|
birth_time_confidence: "declared_window" as const,
|
|
candidate_is_confirmed: false as const,
|
|
},
|
|
result_hash: "fictional-window",
|
|
}),
|
|
runRangeReading: async () => ({
|
|
stableThemes: ["career"],
|
|
sensitiveThemes: ["marriage"],
|
|
claimBoundary: RANGE_READING_COPY.boundary,
|
|
themeSensitivity: {},
|
|
window: null,
|
|
}),
|
|
});
|
|
const result = await tools["run-jyotish-window-consultation"].execute!(
|
|
{ question: "婚姻如何" },
|
|
toolContext,
|
|
) as {
|
|
answer_policy: { minute_sensitive_themes: string[] };
|
|
evidence_contract: { user_facing_limitation: string };
|
|
};
|
|
assert.deepEqual(result.answer_policy.minute_sensitive_themes, ["marriage"]);
|
|
assert.match(result.evidence_contract.user_facing_limitation, /这只是粗看/);
|
|
assert.deepEqual(state.workflowReceipt?.minuteSensitiveThemes, ["marriage"]);
|
|
|
|
const route = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
|
|
assert.match(route, /这只是粗看/);
|
|
assert.match(route, /ACCEPTED_RANGE_READING_INSTRUCTION/);
|
|
assert.match(ACCEPTED_RANGE_READING_INSTRUCTION, /不是已确认的唯一出生分钟/);
|
|
});
|