fix: allow public daily guidance without birth minute
Independent Staging Quality Gate / validate (push) Successful in 14m4s
Independent Staging Quality Gate / publish (push) Successful in 9m22s

This commit is contained in:
Jesse_Chen
2026-08-15 23:55:57 +08:00
parent b6df2d9e82
commit 0d59d51814
10 changed files with 358 additions and 29 deletions
+53 -17
View File
@@ -52,6 +52,10 @@ import {
prepareConsultationRoute,
type PreparedConsultationRoute,
} from "@/lib/consultation-route-service";
import {
loadGeneralDailyPanchangaContext,
type GeneralDailyPanchangaContext,
} from "@/lib/general-daily-panchanga";
import { z } from "zod";
export const runtime = "nodejs";
@@ -86,7 +90,7 @@ const generalChatRequestSchema = z.object({
consultationMode: z.literal("general_no_birth_time"),
question: z.string().trim().min(1).max(500),
theme: consultationDomainSchema,
entrypoint: z.undefined().optional(),
entrypoint: z.literal("daily_starlanguage").optional(),
}).strict();
const chatRequestSchema = z.union([generalChatRequestSchema, chartChatRequestSchema]);
@@ -136,6 +140,16 @@ function currentTimeContext(now = new Date()) {
return `服务端当前时间(权威):${now.toISOString()};中国标准时间(UTC+8):${chinaTime}。涉及“现在、今天、今年、未来几个月”等相对时间时,以此为准。`;
}
function generalDailyContextPrompt(context: GeneralDailyPanchangaContext | null) {
if (!context) return "";
return [
"以下是服务器计算并校验结构后的公共日历证据。只能在其边界内解释,不得补充个人命盘结论。",
"<public-daily-panchanga>",
JSON.stringify(context),
"</public-daily-panchanga>",
].join("\n");
}
function chinaCalendarDate(now: Date) {
return new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString().slice(0, 10);
}
@@ -248,11 +262,7 @@ export async function POST(request: Request) {
}
const requestTime = new Date();
const resolvedQuestion = resolveConsultationQuestion({
visibleQuestion: parsed.data.question,
entrypoint: parsed.data.entrypoint,
currentDate: chinaCalendarDate(requestTime),
});
const currentDate = chinaCalendarDate(requestTime);
const userId = user.id;
const requestId = parsed.data.requestId;
@@ -294,7 +304,7 @@ export async function POST(request: Request) {
return data;
},
beforeReserve: ({ consultationMode }) => createConsultationPlan({
userIntent: resolvedQuestion.modelQuestion,
userIntent: parsed.data.question,
theme: consultationTheme,
consultationMode,
modelCreditCost: sessionModel.creditCost,
@@ -359,6 +369,13 @@ export async function POST(request: Request) {
);
}
const resolvedQuestion = resolveConsultationQuestion({
visibleQuestion: parsed.data.question,
entrypoint: parsed.data.entrypoint,
currentDate,
consultationMode: prepared.consultationMode,
});
const modelSelection = prepared.reservation;
if (modelSelection.status === "unavailable") {
@@ -493,6 +510,7 @@ export async function POST(request: Request) {
consultationMode: ConsultationBirthTimeMode,
history: Array<{ role: "user" | "assistant"; text: string }>,
name: string,
generalDailyContext: GeneralDailyPanchangaContext | null,
) {
const state = createConsultationRuntimeState();
const hooks = createConsultationRuntimeHooks(state);
@@ -592,8 +610,11 @@ export async function POST(request: Request) {
currentTimeContext(requestTime),
name ? `用户称呼:${name}` : "",
consultationMode === "general_no_birth_time"
? "当前是用户明确选择的无出生分钟一般咨询。不得计算或推断个人星盘;不得补 00:00、时段中点或任何候选分钟。"
? generalDailyContext
? "当前是无出生分钟的公共今日趋势咨询。可依据服务器提供的公共 Panchanga 摘要回答,但不得计算或推断个人星盘;不得补 00:00、时段中点或任何候选分钟。"
: "当前是用户明确选择的无出生分钟一般咨询。不得计算或推断个人星盘;不得补 00:00、时段中点或任何候选分钟。"
: "先加载 Jyotish Skill;如需新的个人星盘结论,必须调用服务器绑定的排盘工具。",
generalDailyContextPrompt(generalDailyContext),
resolvedQuestion.modelQuestion,
].filter(Boolean).join("\n"),
},
@@ -606,7 +627,12 @@ export async function POST(request: Request) {
hooks,
};
const workflowReceipt: WorkflowReceipt = consultationMode === "general_no_birth_time"
? { route: "general-no-birth-time", status: "ready", preciseTiming: "blocked", missingLayers: ["birth-minute"] }
? {
route: generalDailyContext ? "general-daily-panchanga" : "general-no-birth-time",
status: "ready",
preciseTiming: "blocked",
missingLayers: ["birth-minute"],
}
: { route: "pending", status: "blocked", preciseTiming: "blocked", missingLayers: [] };
if (consultationMode === "general_no_birth_time") {
@@ -629,7 +655,7 @@ export async function POST(request: Request) {
steps: state.steps,
stepBudget: consultationStepBudgetReceipt(state),
workflow: workflowReceipt,
techniqueTruth: "not-applicable",
techniqueTruth: generalDailyContext ? "public-panchanga-only" : "not-applicable",
});
return streamAgentResponse({
runId: requestId,
@@ -648,7 +674,7 @@ export async function POST(request: Request) {
onComplete: (output, agentExecutionReceipt) => settleRun(() => completeResponse(
output,
mergeUsage(usages),
"not-applicable",
generalDailyContext ? "public-panchanga-only" : "not-applicable",
workflowReceipt,
agentExecutionReceipt,
), undefined),
@@ -731,8 +757,15 @@ export async function POST(request: Request) {
const { history } = parsed.data;
const name = prepared.serverChart?.name ?? parsed.data.name;
const consultationMode: ConsultationBirthTimeMode = prepared.consultationMode;
const generalDailyContext = consultationMode === "general_no_birth_time"
&& parsed.data.entrypoint === "daily_starlanguage"
? await loadGeneralDailyPanchangaContext({
date: currentDate,
reference: prepared.generalDailyReference,
})
: null;
if (shouldUseAgenticRuntime(user)) {
return await runAgenticConsultation(consultationMode, history, name);
return await runAgenticConsultation(consultationMode, history, name, generalDailyContext);
}
if (!shouldRunBirthChartWorkflow(consultationMode)) {
const result = await getGeneralJyotishAgent(selectedModel).stream([
@@ -741,13 +774,16 @@ export async function POST(request: Request) {
content: [
currentTimeContext(requestTime),
name ? `用户称呼:${name}` : "",
"当前是用户明确选择的无出生分钟一般咨询。不得计算或推断个人星盘;不得补 00:00、时段中点或任何候选分钟。",
generalDailyContext
? "当前是无出生分钟的公共今日趋势咨询。可依据服务器提供的公共 Panchanga 摘要回答,但不得计算或推断个人星盘;不得补 00:00、时段中点或任何候选分钟。"
: "当前是用户明确选择的无出生分钟一般咨询。不得计算或推断个人星盘;不得补 00:00、时段中点或任何候选分钟。",
generalDailyContextPrompt(generalDailyContext),
resolvedQuestion.modelQuestion,
].filter(Boolean).join("\n"),
},
]);
const workflowReceipt: WorkflowReceipt = {
route: "general-no-birth-time",
route: generalDailyContext ? "general-daily-panchanga" : "general-no-birth-time",
status: "ready",
preciseTiming: "blocked",
missingLayers: ["birth-minute"],
@@ -757,7 +793,7 @@ export async function POST(request: Request) {
? () => completeResponse(
output,
result.totalUsage,
"not-applicable",
generalDailyContext ? "public-panchanga-only" : "not-applicable",
workflowReceipt,
)
: cancel,
@@ -770,7 +806,7 @@ export async function POST(request: Request) {
headers: {
"x-jyotish-workflow-route": workflowReceipt.route,
"x-jyotish-workflow-status": workflowReceipt.status,
"x-jyotish-technique-truth": "not-applicable",
"x-jyotish-technique-truth": generalDailyContext ? "public-panchanga-only" : "not-applicable",
"x-jyotish-precise-timing": workflowReceipt.preciseTiming,
"x-jyotish-missing-layers": workflowReceipt.missingLayers.join(","),
"x-jyotish-birth-time-mode": consultationMode,
@@ -778,7 +814,7 @@ export async function POST(request: Request) {
onComplete: (rawTransformedText) => settle(() => completeResponse(
rawTransformedText,
result.totalUsage,
"not-applicable",
generalDailyContext ? "public-panchanga-only" : "not-applicable",
workflowReceipt,
)),
onError: (_error, emitted, output: string) => settleErrored(emitted, output),