651 lines
23 KiB
TypeScript
651 lines
23 KiB
TypeScript
import {
|
|
createEmptyAgentModeWorkspaceState,
|
|
normalizeAgentModeWorkspaceState,
|
|
} from "@/lib/agents/mode-workspace-state";
|
|
import {
|
|
createEmptyAgentWorkflowMaterialSelections,
|
|
normalizeAgentWorkflowMaterialSelections,
|
|
} from "@/lib/agents/workflow-material-config";
|
|
import type {
|
|
AgentGenerationProjectPersistedState,
|
|
AgentGenerationProjectReplacementResultState,
|
|
AgentGenerationProjectResultCollections,
|
|
AgentGenerationResultModeType,
|
|
AgentGenerationReplacementResultStatus,
|
|
AgentGenerationProjectWorkspaceState,
|
|
AgentDetailReplacementReferenceItem,
|
|
AgentWorkflowBusinessModeKey,
|
|
AgentWorkflowPersistedState,
|
|
} from "@/types/agent";
|
|
|
|
export function createEmptyAgentGenerationProjectWorkspaceState(): AgentGenerationProjectWorkspaceState {
|
|
return {
|
|
input: "",
|
|
attachments: [],
|
|
selectedModelId: "auto",
|
|
activeBusinessModeKey: "main_sku",
|
|
imageParams: {
|
|
aspectRatio: "auto",
|
|
imageSize: "2K",
|
|
imageCount: 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function createEmptyAgentWorkflowPersistedState(): AgentWorkflowPersistedState {
|
|
return {
|
|
inputs: {
|
|
primarySku: "",
|
|
secondarySku: "",
|
|
brand: "",
|
|
store: "",
|
|
officialSecondCategory: "",
|
|
officialThirdCategory: "",
|
|
thirdCategory: "",
|
|
fourthCategory: "",
|
|
fifthCategory: "",
|
|
priceBand: "",
|
|
},
|
|
materials: createEmptyAgentWorkflowMaterialSelections(),
|
|
appearanceRecognitionEnabled: false,
|
|
selections: {},
|
|
selectedBrandModel: null,
|
|
selectedAgentPresetId: null,
|
|
currentStage: "workflow",
|
|
agentModeState: createEmptyAgentModeWorkspaceState(),
|
|
};
|
|
}
|
|
|
|
export function createEmptyAgentGenerationProjectPersistedState(): AgentGenerationProjectPersistedState {
|
|
return {
|
|
workflowState: createEmptyAgentWorkflowPersistedState(),
|
|
workspaceState: createEmptyAgentGenerationProjectWorkspaceState(),
|
|
resultImageUrls: [],
|
|
resultCollections: {},
|
|
deletedResultCollections: {},
|
|
replacementResultState: {},
|
|
};
|
|
}
|
|
|
|
function normalizeResultCollections(value: unknown): AgentGenerationProjectResultCollections {
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
const normalizeUrlList = (input: unknown) =>
|
|
Array.isArray(input)
|
|
? Array.from(
|
|
new Set(
|
|
input
|
|
.map((item) => String(item || "").trim())
|
|
.filter((item) => /^https?:\/\//i.test(item))
|
|
)
|
|
).slice(0, 128)
|
|
: [];
|
|
|
|
return {
|
|
scene_generation: normalizeUrlList(raw.scene_generation),
|
|
detail_generation: normalizeUrlList(raw.detail_generation),
|
|
detail_replacement: normalizeUrlList(raw.detail_replacement),
|
|
sku_replacement: normalizeUrlList(raw.sku_replacement),
|
|
};
|
|
}
|
|
|
|
const REPLACEMENT_RESULT_STATUSES = new Set<AgentGenerationReplacementResultStatus>([
|
|
"completed",
|
|
"passthrough",
|
|
"fallback",
|
|
]);
|
|
|
|
function normalizeOptionalHttpUrl(value: unknown) {
|
|
const raw = String(value || "").trim();
|
|
return /^https?:\/\//i.test(raw) ? raw : null;
|
|
}
|
|
|
|
function normalizeReplacementResultStatus(value: unknown): AgentGenerationReplacementResultStatus {
|
|
return REPLACEMENT_RESULT_STATUSES.has(value as AgentGenerationReplacementResultStatus)
|
|
? (value as AgentGenerationReplacementResultStatus)
|
|
: "completed";
|
|
}
|
|
|
|
export function normalizeAgentGenerationProjectReplacementResultState(
|
|
value: unknown
|
|
): AgentGenerationProjectReplacementResultState {
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
const normalizeModeState = (input: unknown) => {
|
|
const records =
|
|
input && typeof input === "object" && !Array.isArray(input)
|
|
? (input as Record<string, unknown>)
|
|
: {};
|
|
const entries: Array<
|
|
readonly [
|
|
string,
|
|
NonNullable<AgentGenerationProjectReplacementResultState["detail_replacement"]>[string],
|
|
]
|
|
> = [];
|
|
|
|
Object.entries(records).forEach(([rawCardId, rawRecord]) => {
|
|
const cardId = String(rawCardId || "").trim();
|
|
if (!cardId || !rawRecord || typeof rawRecord !== "object" || Array.isArray(rawRecord)) {
|
|
return;
|
|
}
|
|
|
|
const record = rawRecord as Record<string, unknown>;
|
|
const resultUrl = normalizeOptionalHttpUrl(record.url);
|
|
const text = String(record.text || "").trim();
|
|
const compareSourceUrl = normalizeOptionalHttpUrl(record.compareSourceUrl);
|
|
const referenceImageUrl = normalizeOptionalHttpUrl(record.referenceImageUrl);
|
|
const referencePreviewUrl = normalizeOptionalHttpUrl(record.referencePreviewUrl);
|
|
const label = String(record.label || "").trim() || null;
|
|
const updatedAt = Number(record.updatedAt);
|
|
|
|
if (!resultUrl && !text && !compareSourceUrl && !referenceImageUrl && !referencePreviewUrl) {
|
|
return;
|
|
}
|
|
|
|
entries.push([
|
|
cardId.slice(0, 160),
|
|
{
|
|
url: resultUrl,
|
|
text,
|
|
status: normalizeReplacementResultStatus(record.status),
|
|
updatedAt: Number.isFinite(updatedAt) && updatedAt > 0 ? Math.round(updatedAt) : 0,
|
|
compareSourceUrl,
|
|
referenceImageUrl,
|
|
referencePreviewUrl,
|
|
label,
|
|
},
|
|
]);
|
|
});
|
|
|
|
return Object.fromEntries(entries.slice(0, 256));
|
|
};
|
|
|
|
return {
|
|
detail_replacement: normalizeModeState(raw.detail_replacement),
|
|
sku_replacement: normalizeModeState(raw.sku_replacement),
|
|
};
|
|
}
|
|
|
|
function getReplacementResultUrls(
|
|
value:
|
|
| NonNullable<AgentGenerationProjectReplacementResultState["detail_replacement"]>
|
|
| undefined
|
|
| null
|
|
) {
|
|
return Array.from(
|
|
new Set(
|
|
Object.values(value || {})
|
|
.map((record) => String(record?.url || "").trim())
|
|
.filter((url) => /^https?:\/\//i.test(url))
|
|
)
|
|
).slice(0, 128);
|
|
}
|
|
|
|
function reconcileReplacementResultCollections(params: {
|
|
resultCollections: AgentGenerationProjectResultCollections;
|
|
replacementResultState: AgentGenerationProjectReplacementResultState;
|
|
}) {
|
|
const detailReplacementUrls = getReplacementResultUrls(
|
|
params.replacementResultState.detail_replacement
|
|
);
|
|
const skuReplacementUrls = getReplacementResultUrls(
|
|
params.replacementResultState.sku_replacement
|
|
);
|
|
|
|
if (detailReplacementUrls.length === 0 && skuReplacementUrls.length === 0) {
|
|
return params.resultCollections;
|
|
}
|
|
|
|
return {
|
|
...params.resultCollections,
|
|
detail_replacement:
|
|
detailReplacementUrls.length > 0
|
|
? detailReplacementUrls
|
|
: params.resultCollections.detail_replacement,
|
|
sku_replacement:
|
|
skuReplacementUrls.length > 0
|
|
? skuReplacementUrls
|
|
: params.resultCollections.sku_replacement,
|
|
} satisfies AgentGenerationProjectResultCollections;
|
|
}
|
|
|
|
const REPLACEMENT_MODE_TYPES = [
|
|
"detail_replacement",
|
|
"sku_replacement",
|
|
] as const;
|
|
|
|
function getReplacementReferences(
|
|
workflowState: AgentWorkflowPersistedState,
|
|
modeType: (typeof REPLACEMENT_MODE_TYPES)[number]
|
|
) {
|
|
return modeType === "detail_replacement"
|
|
? workflowState.agentModeState?.detailReplacement?.references || []
|
|
: workflowState.agentModeState?.skuReplacement?.references || [];
|
|
}
|
|
|
|
function reconcileReplacementResultState(params: {
|
|
workflowState: AgentWorkflowPersistedState;
|
|
resultCollections: AgentGenerationProjectResultCollections;
|
|
replacementResultState: AgentGenerationProjectReplacementResultState;
|
|
}) {
|
|
const nextState: AgentGenerationProjectReplacementResultState = {
|
|
detail_replacement: {
|
|
...(params.replacementResultState.detail_replacement || {}),
|
|
},
|
|
sku_replacement: {
|
|
...(params.replacementResultState.sku_replacement || {}),
|
|
},
|
|
};
|
|
|
|
REPLACEMENT_MODE_TYPES.forEach((modeType) => {
|
|
const modeState = nextState[modeType] || {};
|
|
const hasPersistedResultRecord = Object.values(modeState).some((record) =>
|
|
Boolean(record?.url)
|
|
);
|
|
if (hasPersistedResultRecord) {
|
|
return;
|
|
}
|
|
|
|
const resultUrls = params.resultCollections[modeType] || [];
|
|
if (resultUrls.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const references = getReplacementReferences(params.workflowState, modeType);
|
|
const usedCardIds = new Set(Object.keys(modeState));
|
|
const nextModeState = { ...modeState };
|
|
|
|
resultUrls.forEach((resultUrl, index) => {
|
|
const reference = references[index] || null;
|
|
const preferredCardId = String(reference?.id || "").trim();
|
|
const cardId =
|
|
preferredCardId && !usedCardIds.has(preferredCardId)
|
|
? preferredCardId
|
|
: `${modeType}-legacy-result-${index + 1}`;
|
|
usedCardIds.add(cardId);
|
|
nextModeState[cardId] = {
|
|
url: resultUrl,
|
|
text: "",
|
|
status: "completed",
|
|
updatedAt: 0,
|
|
compareSourceUrl: reference?.imageUrl || reference?.previewUrl || null,
|
|
referenceImageUrl: reference?.imageUrl || null,
|
|
referencePreviewUrl: reference?.previewUrl || null,
|
|
label: reference?.label || `历史结果 ${index + 1}`,
|
|
};
|
|
});
|
|
|
|
nextState[modeType] = nextModeState;
|
|
});
|
|
|
|
return nextState;
|
|
}
|
|
|
|
function buildRecoveredReplacementReference(params: {
|
|
cardId: string;
|
|
index: number;
|
|
record: NonNullable<
|
|
AgentGenerationProjectReplacementResultState["detail_replacement"]
|
|
>[string];
|
|
}): AgentDetailReplacementReferenceItem {
|
|
const referenceImageUrl =
|
|
params.record.referenceImageUrl ||
|
|
params.record.referencePreviewUrl ||
|
|
params.record.compareSourceUrl ||
|
|
null;
|
|
return {
|
|
id: params.cardId,
|
|
label: params.record.label || `历史结果 ${params.index + 1}`,
|
|
imageUrl: referenceImageUrl,
|
|
previewUrl: params.record.referencePreviewUrl || referenceImageUrl,
|
|
sortOrder: params.index,
|
|
};
|
|
}
|
|
|
|
function reconcileWorkflowReplacementReferences(params: {
|
|
workflowState: AgentWorkflowPersistedState;
|
|
replacementResultState: AgentGenerationProjectReplacementResultState;
|
|
}) {
|
|
const agentModeState =
|
|
params.workflowState.agentModeState || createEmptyAgentModeWorkspaceState();
|
|
|
|
const reconcileModeReferences = (
|
|
modeType: (typeof REPLACEMENT_MODE_TYPES)[number],
|
|
references: AgentDetailReplacementReferenceItem[]
|
|
) => {
|
|
const existingIds = new Set(references.map((reference) => reference.id));
|
|
const recovered = Object.entries(
|
|
params.replacementResultState[modeType] || {}
|
|
)
|
|
.filter(([cardId, record]) => cardId && Boolean(record?.url) && !existingIds.has(cardId))
|
|
.map(([cardId, record], index) =>
|
|
buildRecoveredReplacementReference({
|
|
cardId,
|
|
index: references.length + index,
|
|
record,
|
|
})
|
|
);
|
|
return recovered.length > 0 ? [...references, ...recovered] : references;
|
|
};
|
|
|
|
const detailReferences = reconcileModeReferences(
|
|
"detail_replacement",
|
|
agentModeState.detailReplacement.references
|
|
);
|
|
const skuReferences = reconcileModeReferences(
|
|
"sku_replacement",
|
|
agentModeState.skuReplacement.references
|
|
);
|
|
|
|
if (
|
|
detailReferences === agentModeState.detailReplacement.references &&
|
|
skuReferences === agentModeState.skuReplacement.references
|
|
) {
|
|
return params.workflowState;
|
|
}
|
|
|
|
return {
|
|
...params.workflowState,
|
|
agentModeState: {
|
|
...agentModeState,
|
|
detailReplacement: {
|
|
...agentModeState.detailReplacement,
|
|
references: detailReferences,
|
|
},
|
|
skuReplacement: {
|
|
...agentModeState.skuReplacement,
|
|
references: skuReferences,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
function inferLegacyResultModeType(
|
|
workflowState: AgentWorkflowPersistedState,
|
|
resultUrls: string[]
|
|
): AgentGenerationResultModeType {
|
|
const detailReferenceCount = workflowState.agentModeState?.detailReplacement?.references?.length || 0;
|
|
const skuReferenceCount = workflowState.agentModeState?.skuReplacement?.references?.length || 0;
|
|
const resultCount = resultUrls.length;
|
|
|
|
if (detailReferenceCount > 0 && skuReferenceCount === 0) {
|
|
return "detail_replacement";
|
|
}
|
|
|
|
if (skuReferenceCount > 0 && detailReferenceCount === 0) {
|
|
return "sku_replacement";
|
|
}
|
|
|
|
if (detailReferenceCount > 0 || skuReferenceCount > 0) {
|
|
const detailGap = Math.abs(resultCount - detailReferenceCount);
|
|
const skuGap = Math.abs(resultCount - skuReferenceCount);
|
|
return detailGap <= skuGap ? "detail_replacement" : "sku_replacement";
|
|
}
|
|
|
|
return "scene_generation";
|
|
}
|
|
|
|
function normalizeMigratedResultCollections(params: {
|
|
workflowState: AgentWorkflowPersistedState;
|
|
resultImageUrls: string[];
|
|
resultCollections: AgentGenerationProjectResultCollections;
|
|
}) {
|
|
const normalizedSceneUrls = params.resultCollections.scene_generation || [];
|
|
const normalizedDetailGenerationUrls = params.resultCollections.detail_generation || [];
|
|
const normalizedDetailUrls = params.resultCollections.detail_replacement || [];
|
|
const normalizedSkuUrls = params.resultCollections.sku_replacement || [];
|
|
const hasExplicitBuckets =
|
|
normalizedSceneUrls.length > 0 ||
|
|
normalizedDetailGenerationUrls.length > 0 ||
|
|
normalizedDetailUrls.length > 0 ||
|
|
normalizedSkuUrls.length > 0;
|
|
|
|
if (!hasExplicitBuckets && params.resultImageUrls.length > 0) {
|
|
const inferredModeType = inferLegacyResultModeType(
|
|
params.workflowState,
|
|
params.resultImageUrls
|
|
);
|
|
return {
|
|
scene_generation:
|
|
inferredModeType === "scene_generation" ? params.resultImageUrls : [],
|
|
detail_replacement:
|
|
inferredModeType === "detail_replacement" ? params.resultImageUrls : [],
|
|
sku_replacement:
|
|
inferredModeType === "sku_replacement" ? params.resultImageUrls : [],
|
|
} satisfies AgentGenerationProjectResultCollections;
|
|
}
|
|
|
|
const detailReferenceCount =
|
|
params.workflowState.agentModeState?.detailReplacement?.references?.length || 0;
|
|
const skuReferenceCount =
|
|
params.workflowState.agentModeState?.skuReplacement?.references?.length || 0;
|
|
|
|
if (
|
|
normalizedSceneUrls.length > 0 &&
|
|
normalizedDetailUrls.length === 0 &&
|
|
normalizedSkuUrls.length === 0
|
|
) {
|
|
if (
|
|
detailReferenceCount > 0 &&
|
|
normalizedSceneUrls.length >= Math.max(4, Math.floor(detailReferenceCount * 0.6))
|
|
) {
|
|
return {
|
|
scene_generation: [],
|
|
detail_generation: [],
|
|
detail_replacement: normalizedSceneUrls,
|
|
sku_replacement: [],
|
|
} satisfies AgentGenerationProjectResultCollections;
|
|
}
|
|
|
|
if (
|
|
skuReferenceCount > 0 &&
|
|
normalizedSceneUrls.length >= Math.max(4, Math.floor(skuReferenceCount * 0.6))
|
|
) {
|
|
return {
|
|
scene_generation: [],
|
|
detail_generation: [],
|
|
detail_replacement: [],
|
|
sku_replacement: normalizedSceneUrls,
|
|
} satisfies AgentGenerationProjectResultCollections;
|
|
}
|
|
}
|
|
|
|
return params.resultCollections;
|
|
}
|
|
|
|
function normalizeWorkspaceState(value: unknown): AgentGenerationProjectWorkspaceState {
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
const attachments = Array.isArray(raw.attachments)
|
|
? raw.attachments
|
|
.map((item) => {
|
|
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
return null;
|
|
}
|
|
const record = item as Record<string, unknown>;
|
|
const url = String(record.url || "").trim();
|
|
if (!url) return null;
|
|
return {
|
|
url,
|
|
name: String(record.name || "").trim() || null,
|
|
contentType: String(record.contentType || "").trim() || null,
|
|
assetId: String(record.assetId || "").trim() || null,
|
|
};
|
|
})
|
|
.slice(0, 16)
|
|
: [];
|
|
|
|
const rawImageParams =
|
|
raw.imageParams && typeof raw.imageParams === "object" && !Array.isArray(raw.imageParams)
|
|
? (raw.imageParams as Record<string, unknown>)
|
|
: {};
|
|
|
|
const imageCount = Number(rawImageParams.imageCount);
|
|
const activeBusinessModeKey = String(raw.activeBusinessModeKey || "").trim();
|
|
const normalizedBusinessModeKey: AgentWorkflowBusinessModeKey =
|
|
activeBusinessModeKey === "detail" ||
|
|
activeBusinessModeKey === "buyer_show" ||
|
|
activeBusinessModeKey === "main_sku"
|
|
? activeBusinessModeKey
|
|
: "main_sku";
|
|
|
|
return {
|
|
input: String(raw.input || "").trim(),
|
|
attachments,
|
|
selectedModelId: String(raw.selectedModelId || "").trim() || "auto",
|
|
activeBusinessModeKey: normalizedBusinessModeKey,
|
|
imageParams: {
|
|
aspectRatio: String(rawImageParams.aspectRatio || "").trim() || "auto",
|
|
imageSize: String(rawImageParams.imageSize || "").trim() || "2K",
|
|
imageCount: Number.isFinite(imageCount) ? Math.max(1, Math.min(8, Math.round(imageCount))) : 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
function hasExplicitWorkspaceBusinessModeKey(value: unknown) {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
|
|
return Boolean(String((value as Record<string, unknown>).activeBusinessModeKey || "").trim());
|
|
}
|
|
|
|
function inferWorkspaceBusinessModeKey(params: {
|
|
workflowState: AgentWorkflowPersistedState;
|
|
resultCollections: AgentGenerationProjectResultCollections;
|
|
replacementResultState: AgentGenerationProjectReplacementResultState;
|
|
}): AgentWorkflowBusinessModeKey {
|
|
const detailReferenceCount =
|
|
params.workflowState.agentModeState?.detailReplacement?.references?.length || 0;
|
|
const skuReferenceCount =
|
|
params.workflowState.agentModeState?.skuReplacement?.references?.length || 0;
|
|
const normalizedResultCollections = normalizeResultCollections(params.resultCollections);
|
|
const hasDetailResults =
|
|
(normalizedResultCollections.detail_generation || []).length > 0 ||
|
|
(normalizedResultCollections.detail_replacement || []).length > 0 ||
|
|
(normalizedResultCollections.sku_replacement || []).length > 0 ||
|
|
getReplacementResultUrls(params.replacementResultState.detail_replacement).length > 0 ||
|
|
getReplacementResultUrls(params.replacementResultState.sku_replacement).length > 0;
|
|
|
|
return detailReferenceCount > 0 || skuReferenceCount > 0 || hasDetailResults
|
|
? "detail"
|
|
: "main_sku";
|
|
}
|
|
|
|
function normalizeWorkflowState(value: unknown): AgentWorkflowPersistedState {
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
return {
|
|
inputs: {
|
|
primarySku: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).primarySku || "" : "").trim(),
|
|
secondarySku: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).secondarySku || "" : "").trim(),
|
|
brand: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).brand || "" : "").trim(),
|
|
store: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).store || "" : "").trim(),
|
|
officialSecondCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).officialSecondCategory || "" : "").trim(),
|
|
officialThirdCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).officialThirdCategory || "" : "").trim(),
|
|
thirdCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).thirdCategory || "" : "").trim(),
|
|
fourthCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).fourthCategory || "" : "").trim(),
|
|
fifthCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).fifthCategory || "" : "").trim(),
|
|
priceBand: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).priceBand || "" : "").trim(),
|
|
},
|
|
materials: normalizeAgentWorkflowMaterialSelections(raw.materials),
|
|
appearanceRecognitionEnabled: raw.appearanceRecognitionEnabled === true,
|
|
selections:
|
|
raw.selections && typeof raw.selections === "object" && !Array.isArray(raw.selections)
|
|
? (raw.selections as Record<string, string>)
|
|
: {},
|
|
selectedBrandModel:
|
|
raw.selectedBrandModel && typeof raw.selectedBrandModel === "object" && !Array.isArray(raw.selectedBrandModel)
|
|
? {
|
|
...(raw.selectedBrandModel as Record<string, unknown>),
|
|
brandName: String((raw.selectedBrandModel as Record<string, unknown>).brandName || "").trim(),
|
|
id: String((raw.selectedBrandModel as Record<string, unknown>).id || "").trim(),
|
|
imageUrl: String((raw.selectedBrandModel as Record<string, unknown>).imageUrl || "").trim(),
|
|
assetId: String((raw.selectedBrandModel as Record<string, unknown>).assetId || "").trim() || null,
|
|
displayName: String((raw.selectedBrandModel as Record<string, unknown>).displayName || "").trim() || null,
|
|
mimeType: String((raw.selectedBrandModel as Record<string, unknown>).mimeType || "").trim() || null,
|
|
sortOrder: Number.isFinite(Number((raw.selectedBrandModel as Record<string, unknown>).sortOrder))
|
|
? Math.max(0, Math.min(999, Math.round(Number((raw.selectedBrandModel as Record<string, unknown>).sortOrder))))
|
|
: 0,
|
|
}
|
|
: null,
|
|
selectedAgentPresetId: String(raw.selectedAgentPresetId || "").trim() || null,
|
|
currentStage: raw.currentStage === "mode" ? "mode" : "workflow",
|
|
agentModeState: normalizeAgentModeWorkspaceState(raw.agentModeState),
|
|
};
|
|
}
|
|
|
|
export function normalizeAgentGenerationProjectPersistedState(
|
|
value: unknown
|
|
): AgentGenerationProjectPersistedState {
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
const resultImageUrls = Array.isArray(raw.resultImageUrls)
|
|
? Array.from(
|
|
new Set(
|
|
raw.resultImageUrls
|
|
.map((item) => String(item || "").trim())
|
|
.filter((item) => /^https?:\/\//i.test(item))
|
|
)
|
|
).slice(0, 64)
|
|
: [];
|
|
let workflowState = normalizeWorkflowState(raw.workflowState);
|
|
const deletedResultCollections = normalizeResultCollections(raw.deletedResultCollections);
|
|
const initialReplacementResultState = normalizeAgentGenerationProjectReplacementResultState(
|
|
raw.replacementResultState
|
|
);
|
|
let resultCollections = reconcileReplacementResultCollections({
|
|
replacementResultState: initialReplacementResultState,
|
|
resultCollections: normalizeMigratedResultCollections({
|
|
workflowState,
|
|
resultImageUrls,
|
|
resultCollections: normalizeResultCollections(raw.resultCollections),
|
|
}),
|
|
});
|
|
const replacementResultState = reconcileReplacementResultState({
|
|
workflowState,
|
|
resultCollections,
|
|
replacementResultState: initialReplacementResultState,
|
|
});
|
|
workflowState = reconcileWorkflowReplacementReferences({
|
|
workflowState,
|
|
replacementResultState,
|
|
});
|
|
resultCollections = reconcileReplacementResultCollections({
|
|
resultCollections,
|
|
replacementResultState,
|
|
});
|
|
const workspaceState = normalizeWorkspaceState(raw.workspaceState);
|
|
if (!hasExplicitWorkspaceBusinessModeKey(raw.workspaceState)) {
|
|
workspaceState.activeBusinessModeKey = inferWorkspaceBusinessModeKey({
|
|
workflowState,
|
|
resultCollections,
|
|
replacementResultState,
|
|
});
|
|
}
|
|
|
|
return {
|
|
workflowState,
|
|
workspaceState,
|
|
resultImageUrls,
|
|
resultCollections,
|
|
deletedResultCollections,
|
|
replacementResultState,
|
|
};
|
|
}
|