ai-chatbot/lib/agents/psd-template-progress.ts
2026-06-04 11:39:53 +08:00

128 lines
3.1 KiB
TypeScript

import "server-only";
import { upsertSystemSetting } from "@/lib/db/queries";
import {
AGENT_PSD_TEMPLATES_KEY,
type AgentPsdTemplate,
} from "@/lib/agents/psd-template-config";
import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings";
const DEFAULT_PARSE_HEARTBEAT_INTERVAL_MS = 25_000;
function clampProgress(value: number) {
return Math.max(0, Math.min(99, Math.round(Number(value || 0))));
}
export async function updateAgentPsdTemplateById(params: {
templateId: string;
updatedBy: string;
updater: (template: AgentPsdTemplate) => AgentPsdTemplate;
}) {
const templates = await readAgentPsdTemplates();
const currentTemplate = templates.find((entry) => entry.id === params.templateId);
if (!currentTemplate) {
return null;
}
const nextTemplate = params.updater(currentTemplate);
await upsertSystemSetting({
key: AGENT_PSD_TEMPLATES_KEY,
value: templates.map((entry) =>
entry.id === params.templateId ? nextTemplate : entry
),
updatedBy: params.updatedBy,
});
return nextTemplate;
}
export async function updateAgentPsdTemplateParseProgress(params: {
templateId: string;
updatedBy: string;
progress: number;
stage: string;
error?: string;
restart?: boolean;
}) {
const now = new Date().toISOString();
return updateAgentPsdTemplateById({
templateId: params.templateId,
updatedBy: params.updatedBy,
updater: (template) => {
if (!params.restart && template.parseStatus !== "processing") {
return template;
}
return {
...template,
parseStatus: "processing",
parseProgress: clampProgress(params.progress),
parseStage: params.stage,
parseError: params.error,
parseStartedAt:
params.restart || !template.parseStartedAt
? now
: template.parseStartedAt,
parseHeartbeatAt: now,
updatedAt: now,
};
},
});
}
export function createAgentPsdTemplateParseHeartbeat(params: {
templateId: string;
updatedBy: string;
getSnapshot: () => {
progress: number;
stage: string;
error?: string;
};
intervalMs?: number;
}) {
let stopped = false;
let inFlight = false;
let failedBeats = 0;
const interval = setInterval(() => {
if (stopped || inFlight) {
return;
}
inFlight = true;
const snapshot = params.getSnapshot();
void updateAgentPsdTemplateParseProgress({
templateId: params.templateId,
updatedBy: params.updatedBy,
progress: snapshot.progress,
stage: snapshot.stage,
error: snapshot.error,
})
.then(() => {
failedBeats = 0;
})
.catch((error) => {
failedBeats += 1;
if (failedBeats <= 3) {
console.warn("[PSD Template] parse heartbeat update failed:", {
templateId: params.templateId,
failedBeats,
error,
});
}
})
.finally(() => {
inFlight = false;
});
}, params.intervalMs || DEFAULT_PARSE_HEARTBEAT_INTERVAL_MS);
return {
stop() {
stopped = true;
clearInterval(interval);
},
};
}