ai-chatbot/lib/canvas/agent-operation-audit.ts

119 lines
3.9 KiB
TypeScript

import type { CanvasOperationPlan } from "@/types/canvas/agent-protocol";
export type CanvasAgentOperationAuditCanvasData = {
metadata?: unknown;
[key: string]: unknown;
};
function normalizeCanvasMetadata(value: unknown) {
return value && typeof value === "object" && !Array.isArray(value)
? { ...(value as Record<string, unknown>) }
: {};
}
export function getCanvasAgentOperationAudit(
canvasData: CanvasAgentOperationAuditCanvasData
) {
const metadata = normalizeCanvasMetadata(canvasData.metadata);
const audit = Array.isArray(metadata.canvasAgentOperationAudit)
? metadata.canvasAgentOperationAudit
: [];
return audit.filter((item) => item && typeof item === "object").slice(-30);
}
export function findCanvasAgentOperationAudit(
canvasData: CanvasAgentOperationAuditCanvasData,
operationId: string
) {
return getCanvasAgentOperationAudit(canvasData).find(
(item) =>
String((item as Record<string, unknown>).operationId || "").trim() ===
operationId
) as Record<string, unknown> | undefined;
}
export function normalizeCanvasAgentAuditStringList(value: unknown) {
return Array.isArray(value)
? value.map((item) => String(item || "").trim()).filter(Boolean)
: [];
}
export function buildIdempotentCanvasAgentAuditResult(params: {
canvasData: CanvasAgentOperationAuditCanvasData;
audit: Record<string, unknown>;
}) {
return {
canvasData: params.canvasData,
createdNodeIds: normalizeCanvasAgentAuditStringList(params.audit.createdNodeIds),
createdNodeIdsByMediaItemId:
params.audit.createdNodeIdsByMediaItemId &&
typeof params.audit.createdNodeIdsByMediaItemId === "object" &&
!Array.isArray(params.audit.createdNodeIdsByMediaItemId)
? Object.fromEntries(
Object.entries(
params.audit.createdNodeIdsByMediaItemId as Record<string, unknown>
)
.map(([mediaItemId, nodeId]) => [
String(mediaItemId || "").trim(),
String(nodeId || "").trim(),
])
.filter(([mediaItemId, nodeId]) => mediaItemId && nodeId)
)
: {},
updatedNodeIds: normalizeCanvasAgentAuditStringList(params.audit.updatedNodeIds),
createdEdgeIds: normalizeCanvasAgentAuditStringList(params.audit.createdEdgeIds),
};
}
export function appendCanvasAgentOperationAudit<T extends CanvasAgentOperationAuditCanvasData>(
params: {
canvasData: T;
auditEntry: Record<string, unknown>;
}
): T {
const metadata = normalizeCanvasMetadata(params.canvasData.metadata);
const audit = getCanvasAgentOperationAudit(params.canvasData);
return {
...params.canvasData,
metadata: {
...metadata,
canvasAgentOperationAudit: [...audit, params.auditEntry].slice(-30),
},
};
}
export function appendCanvasAgentOperationAuditIfMissing<
T extends CanvasAgentOperationAuditCanvasData,
>(params: {
canvasData: T;
plan: CanvasOperationPlan;
result: {
createdNodeIds: string[];
createdNodeIdsByMediaItemId: Record<string, string>;
updatedNodeIds: string[];
createdEdgeIds: string[];
};
}) {
if (findCanvasAgentOperationAudit(params.canvasData, params.plan.operationId)) {
return params.canvasData;
}
return appendCanvasAgentOperationAudit({
canvasData: params.canvasData,
auditEntry: {
operationId: params.plan.operationId,
operation: params.plan.operation,
createdNodeIds: params.result.createdNodeIds,
createdNodeIdsByMediaItemId: params.result.createdNodeIdsByMediaItemId,
updatedNodeIds: params.result.updatedNodeIds,
createdEdgeIds: params.result.createdEdgeIds,
sourceNodeIds: params.plan.sourceNodeIds || [],
targetNodeIds: params.plan.targetNodeIds || [],
targetSlotId: params.plan.targetSlotId,
anchorNodeId: params.plan.anchorNodeId,
appliedAt: new Date().toISOString(),
metadata: params.plan.metadata || {},
},
});
}