feat: unify creation agent chat fill

This commit is contained in:
2026-04-25 10:50:19 +08:00
parent c06bf84d0a
commit 31f350d499
26 changed files with 540 additions and 153 deletions

View File

@@ -1,4 +1,4 @@
/* @vitest-environment jsdom */
/* @vitest-environment jsdom */
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@@ -81,11 +81,12 @@ test('big fish workspace submits quick keyword fill request after two turns', as
/>,
);
await user.click(screen.getByRole('button', { name: '补充剩余关键字' }));
await user.click(screen.getByRole('button', { name: '补充剩余设定' }));
expect(onSubmitMessage).toHaveBeenCalledWith(
expect.objectContaining({
text: '请补充剩余关键字。',
text: '请补充剩余设定。',
quickFillRequested: true,
}),
);
});
@@ -100,5 +101,5 @@ test('big fish workspace hides keyword fill before two turns', () => {
/>,
);
expect(screen.queryByRole('button', { name: '补充剩余关键字' })).toBeNull();
expect(screen.queryByRole('button', { name: '补充剩余设定' })).toBeNull();
});

View File

@@ -4,7 +4,12 @@ import type {
ExecuteBigFishActionRequest,
SendBigFishMessageRequest,
} from '../../../packages/shared/src/contracts/bigFish';
import { createCreationAgentClientMessageId } from '../../services/creation-agent';
import {
buildCreationAgentChatMessage,
createCreationAgentChatQuickActions,
createCreationAgentClientMessageId,
resolveCreationAgentQuickActionMessage,
} from '../../services/creation-agent';
import {
type CreationAgentAnchorView,
type CreationAgentSessionView,
@@ -84,35 +89,30 @@ export function BigFishAgentWorkspace({
isStreamingReply={Boolean(streamingReplyText)}
isBusy={isBusy}
error={error}
quickActions={[
{
key: 'summarize',
label: '总结当前设定',
},
{
key: 'quickFill',
label: '补充剩余关键字',
minTurn: 2,
},
]}
quickActions={createCreationAgentChatQuickActions()}
onBack={onBack}
onSubmitText={(text) => {
onSubmitMessage({
clientMessageId: createCreationAgentClientMessageId('big-fish'),
text,
});
onSubmitMessage(
buildCreationAgentChatMessage({
clientMessageId: createCreationAgentClientMessageId('big-fish'),
text,
}),
);
}}
onPrimaryAction={() => {
onExecuteAction({ action: 'big_fish_compile_draft' });
}}
onQuickAction={(action) => {
onSubmitMessage({
clientMessageId: createCreationAgentClientMessageId('big-fish'),
text:
action.key === 'quickFill'
? '请补充剩余关键字。'
: '请总结一下当前已经成形的大鱼吃小鱼设定。',
});
const quickActionMessage = resolveCreationAgentQuickActionMessage(
action.key,
'请总结一下当前已经成形的大鱼吃小鱼设定。',
);
onSubmitMessage(
buildCreationAgentChatMessage({
clientMessageId: createCreationAgentClientMessageId('big-fish'),
...quickActionMessage,
}),
);
}}
/>
);

View File

@@ -1,8 +1,10 @@
/* @vitest-environment jsdom */
/* @vitest-environment jsdom */
import { fireEvent, render, screen } from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import { createCreationAgentChatQuickActions } from '../../services/creation-agent';
import {
type CreationAgentTheme,
CreationAgentWorkspace,
@@ -243,17 +245,7 @@ test('creation agent workspace shows primary and progress actions at completed p
loadingText="正在准备"
composerPlaceholder="输入消息"
primaryActionLabel="生成结果页"
quickActions={[
{
key: 'summarize',
label: '总结当前设定',
},
{
key: 'quickFill',
label: '补全剩余设定',
minTurn: 2,
},
]}
quickActions={createCreationAgentChatQuickActions()}
onBack={() => {}}
onSubmitText={() => {}}
onPrimaryAction={() => {}}
@@ -262,7 +254,7 @@ test('creation agent workspace shows primary and progress actions at completed p
expect(screen.getByRole('button', { name: '生成结果页' })).toBeTruthy();
expect(screen.getByRole('button', { name: '总结当前设定' })).toBeTruthy();
expect(screen.getByRole('button', { name: '补剩余设定' })).toBeTruthy();
expect(screen.getByRole('button', { name: '补剩余设定' })).toBeTruthy();
});
test('creation agent workspace hides hero copy area when title and summary are absent', () => {

View File

@@ -1,4 +1,4 @@
/* @vitest-environment jsdom */
/* @vitest-environment jsdom */
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@@ -108,11 +108,11 @@ test('workspace enables quick fill after at least two turns and submits quick fi
/>,
);
await user.click(screen.getByRole('button', { name: '补剩余设定' }));
await user.click(screen.getByRole('button', { name: '补剩余设定' }));
expect(onSubmitMessage).toHaveBeenCalledWith(
expect.objectContaining({
text: '请补剩余设定。',
text: '请补剩余设定。',
quickFillRequested: true,
}),
);

View File

@@ -5,8 +5,11 @@ import type {
SendCustomWorldAgentMessageRequest,
} from '../../../packages/shared/src/contracts/customWorldAgent';
import {
buildCreationAgentChatMessage,
createCreationAgentChatQuickActions,
createCreationAgentClientMessageId,
isCreationAgentOperationBusy,
resolveCreationAgentQuickActionMessage,
} from '../../services/creation-agent';
import {
type CreationAgentAnchorView,
@@ -166,13 +169,17 @@ export function CustomWorldAgentWorkspace({
isCreationAgentOperationBusy(activeOperation) || isStreamingReply;
const submitMessage = (text: string, quickFillRequested = false) => {
onSubmitMessage({
clientMessageId: createCreationAgentClientMessageId('custom-world'),
text,
quickFillRequested,
focusCardId: null,
selectedCardIds: [],
});
onSubmitMessage(
buildCreationAgentChatMessage({
clientMessageId: createCreationAgentClientMessageId('custom-world'),
text,
quickFillRequested,
extraPayload: {
focusCardId: null,
selectedCardIds: [],
},
}),
);
};
return (
@@ -186,17 +193,7 @@ export function CustomWorldAgentWorkspace({
streamingReplyText={streamingReplyText}
isStreamingReply={isStreamingReply}
isBusy={isBusy}
quickActions={[
{
key: 'summarize',
label: '总结当前设定',
},
{
key: 'quickFill',
label: '补全剩余设定',
minTurn: 2,
},
]}
quickActions={createCreationAgentChatQuickActions()}
onBack={onBack}
onSubmitText={(text) => {
submitMessage(text);
@@ -207,12 +204,14 @@ export function CustomWorldAgentWorkspace({
});
}}
onQuickAction={(action) => {
if (action.key === 'quickFill') {
submitMessage('请补全剩余设定。', true);
return;
}
submitMessage('请总结一下当前已经成形的世界设定。');
const quickActionMessage = resolveCreationAgentQuickActionMessage(
action.key,
'请总结一下当前已经成形的世界设定。',
);
submitMessage(
quickActionMessage.text,
quickActionMessage.quickFillRequested,
);
}}
/>
);

View File

@@ -1,4 +1,4 @@
/* @vitest-environment jsdom */
/* @vitest-environment jsdom */
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@@ -80,11 +80,12 @@ test('puzzle workspace submits quick keyword fill request after two turns', asyn
/>,
);
await user.click(screen.getByRole('button', { name: '补充剩余关键字' }));
await user.click(screen.getByRole('button', { name: '补充剩余设定' }));
expect(onSubmitMessage).toHaveBeenCalledWith(
expect.objectContaining({
text: '请补充剩余关键字。',
text: '请补充剩余设定。',
quickFillRequested: true,
}),
);
});
@@ -99,5 +100,5 @@ test('puzzle workspace hides keyword fill before two turns', () => {
/>,
);
expect(screen.queryByRole('button', { name: '补充剩余关键字' })).toBeNull();
expect(screen.queryByRole('button', { name: '补充剩余设定' })).toBeNull();
});

View File

@@ -6,7 +6,12 @@ import type {
PuzzleAgentSessionSnapshot,
SendPuzzleAgentMessageRequest,
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
import { createCreationAgentClientMessageId } from '../../services/creation-agent';
import {
buildCreationAgentChatMessage,
createCreationAgentChatQuickActions,
createCreationAgentClientMessageId,
resolveCreationAgentQuickActionMessage,
} from '../../services/creation-agent';
import {
type CreationAgentOperationView,
type CreationAgentSessionView,
@@ -100,35 +105,30 @@ export function PuzzleAgentWorkspace({
isStreamingReply={Boolean(streamingReplyText)}
isBusy={isBusy}
error={error}
quickActions={[
{
key: 'summarize',
label: '总结当前设定',
},
{
key: 'quickFill',
label: '补充剩余关键字',
minTurn: 2,
},
]}
quickActions={createCreationAgentChatQuickActions()}
onBack={onBack}
onSubmitText={(text) => {
onSubmitMessage({
clientMessageId: createCreationAgentClientMessageId('puzzle'),
text,
});
onSubmitMessage(
buildCreationAgentChatMessage({
clientMessageId: createCreationAgentClientMessageId('puzzle'),
text,
}),
);
}}
onPrimaryAction={() => {
onExecuteAction({ action: 'compile_puzzle_draft' });
}}
onQuickAction={(action) => {
onSubmitMessage({
clientMessageId: createCreationAgentClientMessageId('puzzle'),
text:
action.key === 'quickFill'
? '请补充剩余关键字。'
: '请总结一下当前已经成形的拼图设定。',
});
const quickActionMessage = resolveCreationAgentQuickActionMessage(
action.key,
'请总结一下当前已经成形的拼图设定。',
);
onSubmitMessage(
buildCreationAgentChatMessage({
clientMessageId: createCreationAgentClientMessageId('puzzle'),
...quickActionMessage,
}),
);
}}
/>
);

View File

@@ -0,0 +1,58 @@
import { expect, test } from 'vitest';
import {
CREATION_AGENT_QUICK_FILL_MESSAGE,
buildCreationAgentChatMessage,
createCreationAgentChatQuickActions,
resolveCreationAgentQuickActionMessage,
} from './creationAgentChat';
test('creation agent chat exposes the unified summary and quick fill actions', () => {
expect(createCreationAgentChatQuickActions()).toEqual([
{
key: 'summarize',
label: '总结当前设定',
},
{
key: 'quickFill',
label: '补充剩余设定',
minTurn: 2,
},
]);
});
test('creation agent chat resolves quick actions through one message contract', () => {
expect(
resolveCreationAgentQuickActionMessage('quickFill', '请总结当前设定。'),
).toEqual({
text: CREATION_AGENT_QUICK_FILL_MESSAGE,
quickFillRequested: true,
});
expect(
resolveCreationAgentQuickActionMessage('summarize', '请总结当前设定。'),
).toEqual({
text: '请总结当前设定。',
quickFillRequested: false,
});
});
test('creation agent chat builds shared message payload with genre extras', () => {
expect(
buildCreationAgentChatMessage({
clientMessageId: 'message-1',
text: '请补充剩余设定。',
quickFillRequested: true,
extraPayload: {
focusCardId: null,
selectedCardIds: [],
},
}),
).toEqual({
clientMessageId: 'message-1',
text: '请补充剩余设定。',
quickFillRequested: true,
focusCardId: null,
selectedCardIds: [],
});
});

View File

@@ -0,0 +1,63 @@
export const CREATION_AGENT_SUMMARY_ACTION_KEY = 'summarize';
export const CREATION_AGENT_QUICK_FILL_ACTION_KEY = 'quickFill';
export const CREATION_AGENT_SUMMARY_ACTION_LABEL = '总结当前设定';
export const CREATION_AGENT_QUICK_FILL_ACTION_LABEL = '补充剩余设定';
export const CREATION_AGENT_QUICK_FILL_MESSAGE = '请补充剩余设定。';
type CreationAgentChatQuickAction = {
key: string;
label: string;
minTurn?: number;
};
type CreationAgentChatMessageBase = {
clientMessageId: string;
text: string;
quickFillRequested?: boolean;
};
export function createCreationAgentChatQuickActions(): CreationAgentChatQuickAction[] {
return [
{
key: CREATION_AGENT_SUMMARY_ACTION_KEY,
label: CREATION_AGENT_SUMMARY_ACTION_LABEL,
},
{
key: CREATION_AGENT_QUICK_FILL_ACTION_KEY,
label: CREATION_AGENT_QUICK_FILL_ACTION_LABEL,
minTurn: 2,
},
];
}
export function resolveCreationAgentQuickActionMessage(
actionKey: string,
summaryMessage: string,
) {
const quickFillRequested = actionKey === CREATION_AGENT_QUICK_FILL_ACTION_KEY;
return {
text: quickFillRequested ? CREATION_AGENT_QUICK_FILL_MESSAGE : summaryMessage,
quickFillRequested,
};
}
export function buildCreationAgentChatMessage<TExtraPayload extends object = Record<string, never>>({
clientMessageId,
text,
quickFillRequested = false,
extraPayload,
}: {
clientMessageId: string;
text: string;
quickFillRequested?: boolean;
extraPayload?: TExtraPayload;
}): CreationAgentChatMessageBase & TExtraPayload {
return {
...(extraPayload ?? ({} as TExtraPayload)),
clientMessageId,
text,
quickFillRequested,
};
}

View File

@@ -1,2 +1,3 @@
export * from './creationAgentChat';
export * from './creationAgentProgress';
export * from './creationAgentSse';