ai-chatbot/lib/agents/replacement-task-status.ts

183 lines
4.8 KiB
TypeScript

export type AgentReplacementTaskModeType =
| "detail_replacement"
| "sku_replacement";
export type AgentReplacementTaskStatus =
| "pending"
| "running"
| "succeeded"
| "failed";
export interface AgentReplacementTaskSnapshot {
id: string;
modeType: AgentReplacementTaskModeType;
referenceId: string;
messageId?: string | null;
status: AgentReplacementTaskStatus;
error: string | null;
result: unknown;
createdAt: string;
startedAt: string | null;
completedAt: string | null;
queueStatus?: {
queuePosition?: number | null;
sameModelPendingCount?: number | null;
sameModelRunningCount?: number | null;
concurrencyLimit?: number | null;
estimatedWaitMs?: number | null;
} | null;
}
export type AgentReplacementCardStatus =
| "waiting"
| "queued"
| "generating"
| "completed"
| "passthrough"
| "fallback"
| "failed"
| "missing";
function toTimestamp(value: string | null | undefined) {
const timestamp = Date.parse(String(value || ""));
return Number.isFinite(timestamp) ? timestamp : 0;
}
export function buildLatestAgentReplacementTaskMap(params: {
tasks: AgentReplacementTaskSnapshot[];
modeType: AgentReplacementTaskModeType;
}) {
const result = new Map<string, AgentReplacementTaskSnapshot>();
const orderedTasks = [...params.tasks].sort(
(left, right) => toTimestamp(right.createdAt) - toTimestamp(left.createdAt)
);
for (const task of orderedTasks) {
if (
task.modeType !== params.modeType ||
!task.referenceId ||
result.has(task.referenceId)
) {
continue;
}
result.set(task.referenceId, task);
}
return result;
}
export function isAgentReplacementTaskInPendingRun(params: {
task: AgentReplacementTaskSnapshot | null | undefined;
pendingStartedAt?: number | null;
requestMessageId?: string | null;
clockSkewAllowanceMs?: number;
}) {
if (!params.task) {
return false;
}
const taskMessageId = String(params.task.messageId || "").trim();
const requestMessageId = String(params.requestMessageId || "").trim();
if (taskMessageId && requestMessageId) {
return taskMessageId === requestMessageId;
}
if (!Number.isFinite(Number(params.pendingStartedAt))) {
return true;
}
const taskCreatedAt = toTimestamp(params.task.createdAt);
const pendingStartedAt = Number(params.pendingStartedAt);
const clockSkewAllowanceMs = Math.max(0, params.clockSkewAllowanceMs ?? 15_000);
return taskCreatedAt >= pendingStartedAt - clockSkewAllowanceMs;
}
export function getAgentReplacementTaskResultImageUrl(result: unknown) {
if (!result || typeof result !== "object" || Array.isArray(result)) {
return null;
}
const images = (result as { images?: unknown }).images;
if (!Array.isArray(images)) {
return null;
}
for (const image of images) {
if (!image || typeof image !== "object" || Array.isArray(image)) {
continue;
}
const record = image as Record<string, unknown>;
const originalUrl = String(record.originalUrl || "").trim();
if (/^https?:\/\//i.test(originalUrl)) {
return originalUrl;
}
const thumbnailUrl = String(record.thumbnailUrl || "").trim();
if (/^https?:\/\//i.test(thumbnailUrl)) {
return thumbnailUrl;
}
}
return null;
}
export function resolveAgentReplacementCardStatus(params: {
resultUrl: string | null;
persistedResultStatus?: "completed" | "passthrough" | "fallback" | null;
taskStatus?: AgentReplacementTaskStatus | null;
isRequestedInPendingRun: boolean;
isGenerating: boolean;
index: number;
generatedCount: number;
activeRunIndex?: number | null;
}): AgentReplacementCardStatus {
if (params.taskStatus === "pending") {
return "queued";
}
if (params.taskStatus === "running") {
return "generating";
}
if (params.resultUrl) {
if (
params.persistedResultStatus === "passthrough" ||
params.persistedResultStatus === "fallback"
) {
return params.persistedResultStatus;
}
if (params.taskStatus === "failed") {
return "failed";
}
return "completed";
}
if (params.taskStatus === "failed") {
return "failed";
}
if (params.taskStatus === "succeeded") {
return "missing";
}
if (params.isRequestedInPendingRun) {
return "queued";
}
if (
typeof params.activeRunIndex === "number" &&
params.activeRunIndex >= 0 &&
params.generatedCount <= params.activeRunIndex
) {
if (params.isGenerating) {
return params.generatedCount === params.activeRunIndex ? "generating" : "queued";
}
if (params.generatedCount > 0) {
return "missing";
}
}
if (params.isGenerating) {
return params.index === params.generatedCount ? "generating" : "queued";
}
if (params.generatedCount > 0 && params.index >= params.generatedCount) {
return "missing";
}
return "waiting";
}