183 lines
4.8 KiB
TypeScript
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";
|
|
}
|