修复旧版设计 Brief 媒介兼容
This commit is contained in:
@@ -36,7 +36,7 @@ import {
|
|||||||
type ServerBrief = {
|
type ServerBrief = {
|
||||||
version: number;
|
version: number;
|
||||||
status: DesignBrief['status'];
|
status: DesignBrief['status'];
|
||||||
medium: DesignBrief['medium'];
|
medium?: DesignBrief['medium'];
|
||||||
summary: string;
|
summary: string;
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
missing_decision: string | null;
|
missing_decision: string | null;
|
||||||
@@ -219,10 +219,18 @@ const AGENT_RUN_TIMEOUT_MS = 10 * 60_000;
|
|||||||
const DESIGN_EVENT_DELIVERY_BARRIER_TIMEOUT_MS = 1_000;
|
const DESIGN_EVENT_DELIVERY_BARRIER_TIMEOUT_MS = 1_000;
|
||||||
|
|
||||||
function mapBrief(brief: ServerBrief): DesignBrief {
|
function mapBrief(brief: ServerBrief): DesignBrief {
|
||||||
|
const medium = brief.medium ?? null;
|
||||||
|
if (medium !== null && medium !== 'image' && medium !== 'video') {
|
||||||
|
throw new DesignWorkspaceModuleError(
|
||||||
|
502,
|
||||||
|
'DESIGN_WORKSPACE_RESPONSE_INVALID',
|
||||||
|
'AI 设计服务返回了无效的媒介类型',
|
||||||
|
);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
version: brief.version,
|
version: brief.version,
|
||||||
status: brief.status,
|
status: brief.status,
|
||||||
medium: brief.medium,
|
medium,
|
||||||
summary: brief.summary,
|
summary: brief.summary,
|
||||||
ready: brief.ready,
|
ready: brief.ready,
|
||||||
missingDecision: brief.missing_decision,
|
missingDecision: brief.missing_decision,
|
||||||
@@ -357,7 +365,10 @@ function isServerConversation(value: unknown): value is ServerConversation {
|
|||||||
&& Boolean(brief)
|
&& Boolean(brief)
|
||||||
&& Number.isInteger(brief?.version)
|
&& Number.isInteger(brief?.version)
|
||||||
&& ['draft', 'ready', 'confirmed'].includes(String(brief?.status))
|
&& ['draft', 'ready', 'confirmed'].includes(String(brief?.status))
|
||||||
&& (brief?.medium === null || brief?.medium === 'image' || brief?.medium === 'video')
|
&& (brief?.medium === undefined
|
||||||
|
|| brief.medium === null
|
||||||
|
|| brief.medium === 'image'
|
||||||
|
|| brief.medium === 'video')
|
||||||
&& typeof brief?.summary === 'string'
|
&& typeof brief?.summary === 'string'
|
||||||
&& typeof brief?.ready === 'boolean'
|
&& typeof brief?.ready === 'boolean'
|
||||||
&& (brief?.missing_decision === null || typeof brief?.missing_decision === 'string')
|
&& (brief?.missing_decision === null || typeof brief?.missing_decision === 'string')
|
||||||
|
|||||||
@@ -232,6 +232,30 @@ describe('Works Square AI design adapter', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('normalizes a missing Brief medium to null and rejects invalid non-null values', async () => {
|
||||||
|
const { medium: _medium, ...legacyBrief } = serverConversation.brief;
|
||||||
|
const legacyConversation = {
|
||||||
|
...serverConversation,
|
||||||
|
brief: legacyBrief,
|
||||||
|
};
|
||||||
|
const invalidConversation = {
|
||||||
|
...serverConversation,
|
||||||
|
brief: { ...serverConversation.brief, medium: 'audio' },
|
||||||
|
};
|
||||||
|
const fetchMock = vi.fn<typeof fetch>()
|
||||||
|
.mockResolvedValueOnce(jsonResponse(legacyConversation))
|
||||||
|
.mockResolvedValueOnce(jsonResponse(invalidConversation));
|
||||||
|
const adapter = new WorksSquareDesignWorkspace({
|
||||||
|
apiBaseUrl: 'https://square.example',
|
||||||
|
fetchImpl: fetchMock,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(adapter.getConversation('workspace-one', 'conversation-one'))
|
||||||
|
.resolves.toMatchObject({ brief: { medium: null } });
|
||||||
|
await expect(adapter.getConversation('workspace-one', 'conversation-one'))
|
||||||
|
.rejects.toMatchObject({ code: 'DESIGN_WORKSPACE_RESPONSE_INVALID' });
|
||||||
|
});
|
||||||
|
|
||||||
it('submits a conversation turn through the persistent Agent Gateway Session', async () => {
|
it('submits a conversation turn through the persistent Agent Gateway Session', async () => {
|
||||||
const fetchMock = vi.fn<typeof fetch>()
|
const fetchMock = vi.fn<typeof fetch>()
|
||||||
.mockResolvedValueOnce(jsonResponse(serverConversation))
|
.mockResolvedValueOnce(jsonResponse(serverConversation))
|
||||||
@@ -940,6 +964,11 @@ describe('Works Square AI design adapter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reuses one design Agent Session and normalizes matching task events from fresh WebSocket tickets', async () => {
|
it('reuses one design Agent Session and normalizes matching task events from fresh WebSocket tickets', async () => {
|
||||||
|
const { medium: _medium, ...legacyBrief } = serverConversation.brief;
|
||||||
|
const legacyConversation = {
|
||||||
|
...serverConversation,
|
||||||
|
brief: legacyBrief,
|
||||||
|
};
|
||||||
const snapshotTask = {
|
const snapshotTask = {
|
||||||
task_id: 'task-snapshot',
|
task_id: 'task-snapshot',
|
||||||
workspace_id: 'workspace-one',
|
workspace_id: 'workspace-one',
|
||||||
@@ -964,10 +993,21 @@ describe('Works Square AI design adapter', () => {
|
|||||||
workspace_id: 'workspace-one',
|
workspace_id: 'workspace-one',
|
||||||
conversation_id: 'conversation-one',
|
conversation_id: 'conversation-one',
|
||||||
workspace_view_revision: 2,
|
workspace_view_revision: 2,
|
||||||
conversation: serverConversation,
|
conversation: legacyConversation,
|
||||||
generation_tasks: [snapshotTask],
|
generation_tasks: [snapshotTask],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const invalidSnapshotEvent = {
|
||||||
|
...snapshotEvent,
|
||||||
|
sequence: 98,
|
||||||
|
payload: {
|
||||||
|
...snapshotEvent.payload,
|
||||||
|
conversation: {
|
||||||
|
...serverConversation,
|
||||||
|
brief: { ...serverConversation.brief, medium: 'audio' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
const assistantDeltaEvent = {
|
const assistantDeltaEvent = {
|
||||||
session_id: 'session-one',
|
session_id: 'session-one',
|
||||||
sequence: 2,
|
sequence: 2,
|
||||||
@@ -1038,6 +1078,7 @@ describe('Works Square AI design adapter', () => {
|
|||||||
const { sockets, webSocketFactory } = scriptedSockets([
|
const { sockets, webSocketFactory } = scriptedSockets([
|
||||||
{
|
{
|
||||||
frames: [
|
frames: [
|
||||||
|
{ type: 'event', event: invalidSnapshotEvent },
|
||||||
{ type: 'event', event: snapshotEvent },
|
{ type: 'event', event: snapshotEvent },
|
||||||
{ type: 'event', event: malformedDeltaEvent },
|
{ type: 'event', event: malformedDeltaEvent },
|
||||||
{ type: 'event', event: assistantDeltaEvent },
|
{ type: 'event', event: assistantDeltaEvent },
|
||||||
@@ -1077,6 +1118,7 @@ describe('Works Square AI design adapter', () => {
|
|||||||
conversation: expect.objectContaining({
|
conversation: expect.objectContaining({
|
||||||
conversationId: 'conversation-one',
|
conversationId: 'conversation-one',
|
||||||
title: serverConversation.title,
|
title: serverConversation.title,
|
||||||
|
brief: expect.objectContaining({ medium: null }),
|
||||||
messages: [expect.objectContaining({
|
messages: [expect.objectContaining({
|
||||||
text: serverConversation.messages[0].text,
|
text: serverConversation.messages[0].text,
|
||||||
})],
|
})],
|
||||||
|
|||||||
Reference in New Issue
Block a user