feat: add health consultation report pipeline
This commit is contained in:
@@ -20,7 +20,7 @@ import { useRouter } from "next/navigation";
|
||||
import { FileText } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const DEFAULT_REPORT_THEMES = ["career", "marriage", "wealth", "timing"] as const;
|
||||
export const DEFAULT_REPORT_THEMES = ["career", "marriage", "wealth", "timing", "health"] as const;
|
||||
|
||||
export interface PersonalReportCreateRequest {
|
||||
requestId: string;
|
||||
|
||||
@@ -45,7 +45,7 @@ export const TECHNIQUE_STATUSES = ["verified", "partial", "blocked"] as const;
|
||||
export const CONFLICT_STATUSES = ["unresolved", "partial", "resolved"] as const;
|
||||
export const REPORT_ACTION_PRIORITIES = ["now", "next", "watch"] as const;
|
||||
export const REPORT_DOCUMENT_V1_CHART_IDS = ["D1", "D9", "D10"] as const;
|
||||
export const CHART_IDS = ["D1", "D2", "D9", "D10", "D11", "D24"] as const;
|
||||
export const CHART_IDS = ["D1", "D2", "D6", "D8", "D9", "D10", "D11", "D24", "D30"] as const;
|
||||
|
||||
const claimStatusSchema = z.enum(CLAIM_STATUSES);
|
||||
const themeIdSchema = z.string().regex(/^[a-z][a-z0-9_.-]{0,95}$/, "invalid theme id");
|
||||
|
||||
@@ -317,19 +317,22 @@ function readHouses(
|
||||
return houses;
|
||||
}
|
||||
|
||||
const DOCUMENT_VARGA_CHART_IDS = ["D2", "D9", "D10", "D11", "D24"] as const;
|
||||
const DOCUMENT_VARGA_CHART_IDS = ["D2", "D6", "D8", "D9", "D10", "D11", "D24", "D30"] as const;
|
||||
type DocumentVargaChartId = (typeof DOCUMENT_VARGA_CHART_IDS)[number];
|
||||
|
||||
const VARGA_KEY_ALIASES: Readonly<Record<DocumentVargaChartId, readonly string[]>> = {
|
||||
D2: ["D2_Hora", "D2"],
|
||||
D6: ["D6_Shashthamsa", "D6"],
|
||||
D8: ["D8_Ashtamsa", "D8"],
|
||||
D9: ["D9_Navamsa", "D9"],
|
||||
D10: ["D10_Dasamsa", "D10"],
|
||||
D11: ["D11_Rudramsa", "D11"],
|
||||
D24: ["D24_Chaturvimsamsa", "D24_Siddhamsa", "D24"],
|
||||
D30: ["D30_Trimsamsa", "D30"],
|
||||
};
|
||||
|
||||
function canonicalDocumentVargaChartId(rawId: string): DocumentVargaChartId | null {
|
||||
const match = /^(D(?:24|11|10|9|2))(?:[_\s-].*)?$/.exec(rawId.trim().toUpperCase());
|
||||
const match = /^(D(?:30|24|11|10|9|8|6|2))(?:[_\s-].*)?$/.exec(rawId.trim().toUpperCase());
|
||||
return match ? match[1] as DocumentVargaChartId : null;
|
||||
}
|
||||
|
||||
@@ -797,7 +800,7 @@ const SAFE_CELESTIAL_NAMES = new Map([
|
||||
].map((name) => [name, name.charAt(0).toUpperCase() + name.slice(1)]));
|
||||
|
||||
const SAFE_WORKFLOW_TECHNIQUES = new Map<string, string>([
|
||||
["d1", "D1"], ["d2", "D2"], ["d4", "D4"], ["d6", "D6"], ["d7", "D7"],
|
||||
["d1", "D1"], ["d2", "D2"], ["d4", "D4"], ["d6", "D6"], ["d7", "D7"], ["d8", "D8"],
|
||||
["d9", "D9"], ["d10", "D10"], ["d11", "D11"], ["d12", "D12"],
|
||||
["d24", "D24"], ["d30", "D30"], ["a7", "A7"], ["a10", "A10"],
|
||||
["ul", "UL"], ["upapada", "UL"], ["dk", "DK"], ["darakaraka", "DK"],
|
||||
@@ -1479,6 +1482,7 @@ const NARRATIVE_SNAPSHOT_THEMES: Readonly<Record<string, string>> = {
|
||||
career_narrative: "career",
|
||||
relationship_narrative: "marriage",
|
||||
finance_narrative: "wealth",
|
||||
health_narrative: "health_pressure",
|
||||
timing_narrative: "timing",
|
||||
};
|
||||
|
||||
@@ -1924,11 +1928,13 @@ export function buildReportEvidenceBundleV2(
|
||||
const allVerified = uniqueMatched.every((receipt) => receipt.status === "verified");
|
||||
// The assertionLevel derivation is unchanged: richer content must never
|
||||
// raise certainty. Only the seed-less fallback may lower it.
|
||||
const derivedLevel: ClaimStatus = allVerified && uniqueMatched.length >= 2
|
||||
? "multi_system_consensus"
|
||||
: allVerified
|
||||
? "single_system_inference"
|
||||
: "parameter_sensitive";
|
||||
const derivedLevel: ClaimStatus = plan.theme === "health_pressure"
|
||||
? "parameter_sensitive"
|
||||
: allVerified && uniqueMatched.length >= 2
|
||||
? "multi_system_consensus"
|
||||
: allVerified
|
||||
? "single_system_inference"
|
||||
: "parameter_sensitive";
|
||||
const seed = buildThemeSeed(plan.theme);
|
||||
if (seed) themeNarrativeSeeds.push(seed);
|
||||
const conclusion = seed
|
||||
@@ -2614,7 +2620,7 @@ export function assembleReportDocumentV2(
|
||||
const thematicRefs = uniqueInOrder(thematicNarrative.flatMap((section) => section.evidenceRefs));
|
||||
const executiveRefs = thematicRefs.length > 0 ? thematicRefs : d1Refs;
|
||||
|
||||
const chartIds = new Set(["D1", "D2", "D9", "D10", "D11", "D24"]);
|
||||
const chartIds = new Set(["D1", "D2", "D6", "D8", "D9", "D10", "D11", "D24", "D30"]);
|
||||
const charts: ReportDocumentV2["charts"] = [];
|
||||
for (const chart of bundle.charts) {
|
||||
if (!chartIds.has(chart.id)) continue;
|
||||
|
||||
@@ -36,7 +36,7 @@ import type {
|
||||
PersonalReportService,
|
||||
} from "./personal-report-service-core";
|
||||
|
||||
const reportRequestThemes = z.enum(["career", "marriage", "wealth", "timing", "general"]);
|
||||
const reportRequestThemes = z.enum(["career", "marriage", "wealth", "health", "timing", "general"]);
|
||||
|
||||
export const personalReportCreateRequestSchema = z.object({
|
||||
requestId: z.string().uuid(),
|
||||
@@ -45,7 +45,7 @@ export const personalReportCreateRequestSchema = z.object({
|
||||
reportType: z.enum(["personal_full", "personal_thematic"]),
|
||||
presentationMode: z.enum(["default", "research"]).default("default"),
|
||||
depth: z.enum(["concise", "standard", "deep", "research"]).default("standard"),
|
||||
themes: z.array(reportRequestThemes).min(1).max(6).default(["career", "marriage", "wealth", "timing"]),
|
||||
themes: z.array(reportRequestThemes).min(1).max(6).default(["career", "marriage", "wealth", "timing", "health"]),
|
||||
}).strict();
|
||||
|
||||
export type PersonalReportCreateRequest = z.infer<typeof personalReportCreateRequestSchema>;
|
||||
|
||||
@@ -266,13 +266,13 @@ const deterministicClaimBoundarySchema = z.enum([
|
||||
"relationship_combinations",
|
||||
]);
|
||||
const allowedTechniqueNames = new Set([
|
||||
"D1", "D2", "D4", "D6", "D7", "D9", "D10", "D11", "D12", "D24", "D30",
|
||||
"D1", "D2", "D4", "D6", "D7", "D8", "D9", "D10", "D11", "D12", "D24", "D30",
|
||||
"A7", "A10", "UL", "DK", "AmK",
|
||||
"Vimshottari", "Narayana", "Transit", "Yoga", "Ashtakavarga",
|
||||
"Functional Benefic/Malefic", "Planet Degrees", "House Degrees",
|
||||
"Planetary Friendship",
|
||||
]);
|
||||
const missingTechniquePattern = /^(?:career|marriage|wealth|education|migration_home|family|health_pressure|timing|general):(?:D1|D2|D4|D6\/D30|D7\/D12|D9|D10|D11|D12|D24|A7|A10|UL|DK|AmK|Vimshottari|Narayana|Transit|Yoga|Ashtakavarga)$/;
|
||||
const missingTechniquePattern = /^(?:career|marriage|wealth|education|migration_home|family|health_pressure|timing|general):(?:D1|D2|D4|D6|D6\/D30|D7\/D12|D8|D9|D10|D11|D12|D24|D30|A7|A10|UL|DK|AmK|Vimshottari|Narayana|Transit|Yoga|Ashtakavarga)$/;
|
||||
const techniqueSchema = z.string().max(80).refine(
|
||||
(value) => allowedTechniqueNames.has(value) || missingTechniquePattern.test(value),
|
||||
"report_bundle_technique_not_allowlisted",
|
||||
|
||||
@@ -48,7 +48,8 @@ export const THEME_INTERPRETATION_PACKS: Readonly<Record<string, string>> = {
|
||||
· 代际张力写成"两代人对安全感的定义不同",并给出可操作的边界设定方式。
|
||||
禁止:不预测生育时间、性别或数量;不判断亲属健康与寿数;不建议断绝或维持任何具体亲属关系。`,
|
||||
|
||||
health_pressure: `【健康压力主题·解读要点(非医疗)】
|
||||
health_pressure: `非医疗边界:本章只讨论压力、负荷与恢复节奏,不构成诊断或治疗建议。
|
||||
【健康压力主题·解读要点(非医疗)】
|
||||
主指标:第6宫(日常负荷与恢复)、第1宫(体感与精力基线)、第8宫(长期消耗与转化)、压力相关分盘。
|
||||
· 全篇只谈"压力来源、负荷节奏与恢复方式",不谈器官、疾病、症状或诊断。
|
||||
· 第6宫写成"每天消耗你的事情的形状",第1宫写成"精力的基线与恢复速度"。
|
||||
|
||||
@@ -104,9 +104,12 @@ const PLAN: Readonly<Record<ReportTheme, ReportThemeEvidencePlan>> = {
|
||||
section: "健康压力(非医疗)",
|
||||
requiredTechniqueGroups: [
|
||||
{ label: "D1", anyOf: ["D1"] },
|
||||
{ label: "D6/D30", anyOf: ["D6", "D30"] },
|
||||
{ label: "D6", anyOf: ["D6"] },
|
||||
{ label: "D8", anyOf: ["D8"] },
|
||||
{ label: "D30", anyOf: ["D30"] },
|
||||
{ label: "Vimshottari", anyOf: ["Vimshottari", "dasha_boundaries"] },
|
||||
],
|
||||
chartIds: ["D1", "D6", "D30"],
|
||||
chartIds: ["D1", "D6", "D8", "D30"],
|
||||
},
|
||||
timing: {
|
||||
theme: "timing",
|
||||
|
||||
@@ -39,7 +39,7 @@ export const claimStatusSchema = z.enum([
|
||||
]);
|
||||
|
||||
export type ReportVargaHouses = Readonly<{
|
||||
id: "D2" | "D9" | "D10" | "D11" | "D24";
|
||||
id: "D2" | "D6" | "D8" | "D9" | "D10" | "D11" | "D24" | "D30";
|
||||
houses: readonly ReportChartHouse[];
|
||||
}>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user