fix: keep foreground consultations responsive

This commit is contained in:
Jesse_Chen
2026-08-11 11:59:14 +08:00
parent 8eed0603f9
commit 620131d6a8
9 changed files with 191 additions and 36 deletions
+1 -1
View File
@@ -480,7 +480,7 @@ export async function POST(request: Request) {
theme: parsed.data.theme,
});
const workflowContext = applyBirthTimeModeToWorkflowContext(
await runConsultationWorkflow(toolInput),
await runConsultationWorkflow(toolInput, { foreground: true }),
consultationMode,
);
const workflowReceipt = consultationWorkflowReceipt(workflowContext);
+5 -1
View File
@@ -55,7 +55,10 @@ const apiBase = process.env.JYOTISH_API_BASE ?? "http://127.0.0.1:5200";
const jyotishSkillPath = process.env.JYOTISH_SKILL_PATH?.trim()
|| path.resolve(process.cwd(), "..", "skills", "jyotish-vedic-astrology");
export async function runConsultationWorkflow(input: ConsultationInput) {
export async function runConsultationWorkflow(
input: ConsultationInput,
options?: { foreground?: boolean },
) {
const { entryMode, question, theme, ...workflowInput } = input;
const workflowRequest = projectConsultationWorkflowRequest(question, theme);
const response = await fetch(`${apiBase}/api/consultation_workflow`, {
@@ -67,6 +70,7 @@ export async function runConsultationWorkflow(input: ConsultationInput) {
question: workflowRequest.question,
question_text: workflowRequest.question,
theme: workflowRequest.themes,
defer_optional_external_evidence: options?.foreground === true,
}),
signal: AbortSignal.timeout(90_000),
});
@@ -0,0 +1,5 @@
begin;
grant select on table public.consultation_requests to service_role;
commit;
@@ -6,6 +6,7 @@ const read = (path: string) => readFileSync(new URL(`../${path}`, import.meta.ur
const consultRoute = read("src/app/api/consult/route.ts");
const statusRoute = read("src/app/api/consult/status/route.ts");
const migration = read("supabase/migrations/20260808030000_consultation_stream_recovery.sql");
const statusReadMigration = read("supabase/migrations/20260811010000_consultation_status_service_role_read.sql");
test("reserves usage and binds the owned consultation session atomically", () => {
assert.match(migration, /add column if not exists session_id uuid references public\.chat_sessions\(id\) on delete set null/i);
@@ -75,6 +76,11 @@ test("status endpoint supports one global reserved lookup and strict bound polli
assert.doesNotMatch(statusRoute, /String\(data\.response_message\)|responseMessage:\s*data\.response_message as string/);
});
test("status polling grants consultation request reads only to the server role", () => {
assert.match(statusReadMigration, /grant select on table public\.consultation_requests to service_role/i);
assert.doesNotMatch(statusReadMigration, /grant select[\s\S]*to (anon|authenticated)/i);
});
test("detached completion and cancellation use a bounded retry ceiling", () => {
assert.match(consultRoute, /const detachedSettlementAttempts = 3/);
assert.match(consultRoute, /ponytail: Staging MVP ceiling—without a queue\/worker/);
@@ -3,20 +3,27 @@ import { readFileSync } from "node:fs";
import test from "node:test";
const route = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
const reportsRoute = readFileSync(new URL("../src/app/api/reports/route.ts", import.meta.url), "utf8");
const mastra = readFileSync(new URL("../src/mastra/index.ts", import.meta.url), "utf8");
test("runs the Jyotish workflow before streaming a commercial consultation", () => {
const chartBranch = route.slice(route.indexOf("const toolInput = consultationInputSchema.parse"));
assert.match(route, /runConsultationWorkflow/);
assert.match(chartBranch, /await runConsultationWorkflow\(toolInput\)/);
assert.match(chartBranch, /await runConsultationWorkflow\(toolInput, \{ foreground: true \}\)/);
assert.match(chartBranch, /getJyotishAgent\(selectedModel, workflowContext\)\.stream/);
assert.ok(
chartBranch.indexOf("await runConsultationWorkflow(toolInput)")
chartBranch.indexOf("await runConsultationWorkflow(toolInput, { foreground: true })")
< chartBranch.indexOf("getJyotishAgent(selectedModel, workflowContext).stream"),
);
});
test("defers optional external evidence only for foreground chat", () => {
assert.match(mastra, /defer_optional_external_evidence: options\?\.foreground === true/);
assert.match(reportsRoute, /runWorkflow: \(input\) => runConsultationWorkflow\(input\)/);
assert.doesNotMatch(reportsRoute, /foreground:\s*true/);
});
test("grounds the answer in the server-computed workflow without a second tool run", () => {
assert.match(mastra, /function getJyotishAgent\(model: ResolvedLanguageModel, workflowContext\?/);
assert.match(mastra, /workflowContext \? \{\} : \{ consultationTool \}/);
@@ -47,6 +47,7 @@ test("local PostgreSQL applies the reviewed business schema and serves authentic
assert.match(migration.stdout, /applied 20260806030000_settle_order_usage_authorization\.sql/);
assert.match(migration.stdout, /applied 20260806040000_model_configuration\.sql/);
assert.match(migration.stdout, /applied 20260806050000_operations_feature_flags\.sql/);
assert.match(migration.stdout, /applied 20260811010000_consultation_status_service_role_read\.sql/);
assert.equal(
fixture.psql(`
@@ -84,6 +85,15 @@ test("local PostgreSQL applies the reviewed business schema and serves authentic
`),
"true:f",
);
assert.equal(
fixture.psql(`
select
has_table_privilege('service_role', 'public.consultation_requests', 'select') || ':' ||
has_table_privilege('anon', 'public.consultation_requests', 'select') || ':' ||
has_table_privilege('authenticated', 'public.consultation_requests', 'select')
`),
"true:f:f",
);
assert.equal(
fixture.psql(`