feat: add persistent personal report document v2

This commit is contained in:
Jesse_Chen
2026-08-15 05:10:37 +08:00
parent 9d2d79801d
commit 5012ff7212
48 changed files with 9301 additions and 932 deletions
+5 -22
View File
@@ -1,4 +1,4 @@
import { after, NextResponse } from "next/server";
import { NextResponse } from "next/server";
import { runConsultationWorkflow } from "@/mastra";
import { createPersonalReportAgent } from "@/mastra/personal-report";
import { loadLanguageModelCatalog } from "@/lib/model-catalog";
@@ -20,19 +20,20 @@ import {
createSupabasePersonalReportService,
type PersonalReportService,
} from "@/lib/personal-report-service";
import { createSupabasePersonalReportJobService } from "@/lib/personal-report-job-service";
import { createAdminSupabaseClient } from "@/lib/supabase/admin";
import { isSupabaseConfigurationError } from "@/lib/supabase/config";
import { createServerSupabaseClient } from "@/lib/supabase/server";
export const runtime = "nodejs";
export const maxDuration = 120;
const STALE_GENERATION_MS = 15 * 60 * 1000;
const REPORT_LIST_COLUMNS = [
"id",
"request_id",
"report_type",
"presentation_mode",
"depth",
"requested_themes",
"status",
"failure_code",
@@ -56,6 +57,7 @@ function listReportView(value: unknown) {
requestId: typeof row.request_id === "string" ? row.request_id : "",
reportType: typeof row.report_type === "string" ? row.report_type : "personal_full",
presentationMode: typeof row.presentation_mode === "string" ? row.presentation_mode : "default",
depth: typeof row.depth === "string" ? row.depth : "standard",
themes: Array.isArray(row.requested_themes)
? row.requested_themes.filter((theme): theme is string => typeof theme === "string")
: [],
@@ -113,25 +115,6 @@ export async function POST(request: Request) {
const persistence: PersonalReportService = createSupabasePersonalReportService(admin);
const adminDataClient = createPersonalReportDataClient(admin);
// `after()` is bounded by the route duration and can be interrupted by a
// process restart. Reclaim only rows far beyond that bound so a killed
// task never blocks the user's next explicit generation forever.
if (userId) {
const now = new Date();
const staleBefore = new Date(now.getTime() - STALE_GENERATION_MS).toISOString();
const { error } = await admin
.from("personal_reports")
.update({
status: "failed",
failure_code: REPORT_STABLE_CODES.calculationUnavailable,
updated_at: now.toISOString(),
})
.eq("user_id", userId)
.eq("status", "generating")
.lte("updated_at", staleBefore);
if (error) throw error;
}
let profile: unknown = null;
let profileError: unknown = null;
if (userId) {
@@ -208,7 +191,7 @@ export async function POST(request: Request) {
runWorkflow: (input) => runConsultationWorkflow(input),
createAgent: (model) => createPersonalReportAgent(model as Parameters<typeof createPersonalReportAgent>[0]),
skillSnapshot: resolveSkillSnapshot(),
deferGeneration: (task) => after(task),
jobs: createSupabasePersonalReportJobService(admin),
};
const response = await resolveReportCreate(deps);