import type { GenerationTask } from "@/services/api/task-center"; import { DREAMINA_SUBMIT_ERROR_MESSAGES } from "@/lib/generation-error"; import { LOCAL_DREAMINA_FAILED_OR_CANCELLED_MESSAGE, LOCAL_DREAMINA_OFFICIAL_INCOMPLETE_CODE, LOCAL_DREAMINA_OFFICIAL_INCOMPLETE_MESSAGE, LOCAL_DREAMINA_WAIT_STOPPED_CODE, LOCAL_DREAMINA_WAIT_STOPPED_MESSAGE, type LocalDreaminaGenerationTask, } from "@/services/local-dreamina-generation"; export type LocalDreaminaTaskContext = Pick & Partial>; export type LocalDreaminaActionTarget = Pick & Partial>; export const LOCAL_DREAMINA_BACKGROUND_MESSAGE = "任务已转入后台,官方状态会继续同步。"; export type LocalDreaminaDiagnosticLog = { level: "info" | "warn" | "error"; stage: string; observedAt: string; provenance: "task_state" | "provider_observation" | "background_reconcile" | "manual_refresh"; errorCode?: string; }; export function projectLocalDreaminaDiagnosticLog(input: Record): LocalDreaminaDiagnosticLog { const level = input.level === "warn" || input.level === "error" ? input.level : "info"; const stage = typeof input.stage === "string" && /^[a-z][a-z0-9_]{1,80}$/.test(input.stage) ? input.stage : "unknown"; const provenance = input.provenance === "provider_observation" || input.provenance === "background_reconcile" || input.provenance === "manual_refresh" ? input.provenance : "task_state"; const observedAt = typeof input.observedAt === "string" && Number.isFinite(Date.parse(input.observedAt)) ? input.observedAt : new Date(0).toISOString(); return { level, stage, ...(typeof input.errorCode === "string" && /^[a-z][a-z0-9_]{2,80}$/.test(input.errorCode) ? { errorCode: input.errorCode } : {}), provenance, observedAt, }; } export function projectLocalDreaminaTask(task: LocalDreaminaGenerationTask, context?: LocalDreaminaTaskContext): GenerationTask { const status = projectedStatus(task); const terminal = task.lifecycle ? task.lifecycle === "TERMINAL" : status === "succeeded" || status === "failed" || status === "cancelled"; const projectId = projectedProjectId(task, context); const localWaitStopped = task.errorCode === LOCAL_DREAMINA_WAIT_STOPPED_CODE && !isLocalDreaminaBackgroundTask({ id: localDreaminaTaskId(task.id), provider: "dreamina-cli", status, stage: task.stage, receiptRecorded: task.receiptRecorded, }); const officialIncomplete = task.errorCode === LOCAL_DREAMINA_OFFICIAL_INCOMPLETE_CODE; const failedOrCancelled = task.terminalOutcome === "FAILED_OR_CANCELLED"; const submitFailure = task.errorCode ? DREAMINA_SUBMIT_ERROR_MESSAGES[task.errorCode] : undefined; const scopedContext = task.context?.scope === "scoped" ? task.context : undefined; const clientContext = scopedContext ? { ...(scopedContext.nodeId ? { nodeId: scopedContext.nodeId } : {}), ...(scopedContext.conversationId ? { conversationId: scopedContext.conversationId } : {}), ...(scopedContext.messageId ? { messageId: scopedContext.messageId } : {}), ...(scopedContext.batchIndex !== undefined ? { batchIndex: scopedContext.batchIndex } : {}), ...(scopedContext.batchCount !== undefined ? { batchCount: scopedContext.batchCount } : {}), } : undefined; return { id: localDreaminaTaskId(task.id), ...(task.clientOperationId ? { clientOperationId: task.clientOperationId } : {}), ...(scopedContext?.retryOf ? { retryOf: scopedContext.retryOf } : {}), ...(scopedContext?.attemptGroupId ? { attemptGroupId: scopedContext.attemptGroupId } : {}), ...(Object.keys(clientContext ?? {}).length ? { clientContext } : {}), ...(projectId ? { projectId } : {}), type: context?.type ?? `canvas_${task.mode}`, status, progress: task.progress, stage: task.stage, prompt: context?.prompt ?? "", operation: task.operation, provider: "dreamina-cli", model: `local:dreamina-cli:${task.model}`, errorCode: task.errorCode, officialStatus: task.officialStatus, receiptRecorded: task.receiptRecorded, ...(localWaitStopped ? { error: LOCAL_DREAMINA_WAIT_STOPPED_MESSAGE, providerCancelStatus: "uncertain" as const, providerCancelError: LOCAL_DREAMINA_WAIT_STOPPED_MESSAGE, } : failedOrCancelled ? { error: LOCAL_DREAMINA_FAILED_OR_CANCELLED_MESSAGE } : task.officialStatus === "failed" ? { error: LOCAL_DREAMINA_FAILED_OR_CANCELLED_MESSAGE } : task.officialStatus === "cancelled" ? { error: "官方返回状态:cancelled" } : officialIncomplete ? { error: LOCAL_DREAMINA_OFFICIAL_INCOMPLETE_MESSAGE } : submitFailure ? { error: submitFailure } : {}), ...(task.result ? { resultJson: JSON.stringify(task.result) } : {}), ...(task.resultState ? { resultState: task.resultState } : {}), ...(task.outputs ? { outputs: task.outputs.map((output) => ({ ...output })) } : {}), attempts: context?.attempts ?? 1, ...(context?.startedAt ? { startedAt: context.startedAt } : {}), ...(terminal ? { completedAt: task.updatedAt } : {}), createdAt: task.createdAt, updatedAt: task.updatedAt, }; } function projectedStatus(task: LocalDreaminaGenerationTask): GenerationTask["status"] { if (!task.lifecycle) return task.status; if (task.lifecycle === "QUEUED_LOCAL") return "queued"; if (task.lifecycle === "SUBMITTING" || task.lifecycle === "ACCEPTED" || task.lifecycle === "RUNNING") return "running"; if (task.lifecycle === "SUBMISSION_UNCERTAIN") return "failed"; if (task.terminalOutcome === "SUCCEEDED") return "succeeded"; if (task.terminalOutcome === "CANCELLED") return "cancelled"; return "failed"; } function projectedProjectId(task: LocalDreaminaGenerationTask, context?: LocalDreaminaTaskContext) { if (!task.context) return context?.projectId; if (task.context.scope === "legacy_unscoped") return undefined; return task.context.projectId; } export function isLocalDreaminaWaitStopped(task: Pick & Partial>) { return task.provider === "dreamina-cli" && task.errorCode === LOCAL_DREAMINA_WAIT_STOPPED_CODE; } export function isLocalDreaminaSubmissionUncertain(task: LocalDreaminaActionTarget) { return isLocalDreaminaTaskId(task.id) && task.receiptRecorded !== true && (task.stage === "submission_unknown" || task.errorCode === "dreamina_submission_unknown"); } export function isLocalDreaminaBackgroundTask(task: LocalDreaminaActionTarget) { return isLocalDreaminaTaskId(task.id) && task.status === "running" && task.receiptRecorded === true; } export function localDreaminaCancellationMessage(task: LocalDreaminaActionTarget) { if (isLocalDreaminaSubmissionUncertain(task)) return task.error || "提交结果待确认"; if (isLocalDreaminaBackgroundTask(task)) return LOCAL_DREAMINA_BACKGROUND_MESSAGE; return isLocalDreaminaWaitStopped(task) ? LOCAL_DREAMINA_WAIT_STOPPED_MESSAGE : task.error || "任务已取消"; } export function localDreaminaCancellationCopy(task: LocalDreaminaActionTarget) { if (!isLocalDreaminaTaskId(task.id) || isLocalDreaminaSubmissionUncertain(task)) return undefined; if (isLocalDreaminaBackgroundTask(task)) { return { kind: "background", action: "转入后台", confirmation: "任务已由官方接受;转入后台后仍会继续同步官方状态。", } as const; } return { kind: "cancel", action: "取消任务", confirmation: "任务尚未提交官方,可以安全取消本机任务。", } as const; } export function localDreaminaDetachOutcome(task: LocalDreaminaActionTarget) { if (!isLocalDreaminaTaskId(task.id) || isLocalDreaminaSubmissionUncertain(task)) return undefined; if (isLocalDreaminaBackgroundTask(task)) { return { kind: "background", message: LOCAL_DREAMINA_BACKGROUND_MESSAGE, taskStatus: "running", creationStatus: "pending", canvasNodeStatus: "loading", batchItemStatus: "running", } as const; } return { kind: "cancel", message: isLocalDreaminaWaitStopped(task) ? LOCAL_DREAMINA_WAIT_STOPPED_MESSAGE : task.error || "任务已取消", taskStatus: task.status, creationStatus: "cancelled", canvasNodeStatus: "error", batchItemStatus: "cancelled", } as const; } export function localDreaminaTaskId(idempotencyKey: string) { return `dreamina:${stripLocalDreaminaTaskPrefix(idempotencyKey)}`; } export function stripLocalDreaminaTaskPrefix(taskId: string) { return taskId.startsWith("dreamina:") ? taskId.slice("dreamina:".length) : taskId; } export function isLocalDreaminaTaskId(taskId: string) { return /^dreamina:[A-Za-z0-9._:-]{16,120}$/.test(taskId); }