diff --git a/frontend/src/components/chat/GeneratedImages.test.tsx b/frontend/src/components/chat/GeneratedImages.test.tsx
index ef6d2e06..318f6ad5 100644
--- a/frontend/src/components/chat/GeneratedImages.test.tsx
+++ b/frontend/src/components/chat/GeneratedImages.test.tsx
@@ -132,6 +132,7 @@ describe('GeneratedImages', () => {
const card = screen.getByTestId('generated-image-pending');
expect(card).toHaveStyle({ width: `${imageDisplayWidth(2, 3)}px` });
expect(screen.getByRole('status')).toHaveTextContent('正在生成图片');
+ expect(screen.getByText('正在生成图片')).toHaveClass('sr-only');
expect(screen.getByText('12 秒')).toBeInTheDocument();
act(() => {
@@ -179,4 +180,17 @@ describe('collectPendingImageGenerations', () => {
]);
expect(collectPendingImageGenerations(null, [])).toEqual([]);
});
+
+ it('drops a failed card once a later generation replaces it', () => {
+ const failed = { status: 'failed' as const, resultSummary: { kind: 'generated_image', truncated: false, error_code: 'no_image_returned' } as never };
+ const retrying = runWith([call(failed), call({ toolCallId: 'call-2' })]);
+ expect(collectPendingImageGenerations(retrying, [])).toMatchObject([{ toolCallId: 'call-2', status: 'running' }]);
+
+ const retried = runWith([call(failed), call({
+ toolCallId: 'call-2',
+ status: 'success',
+ resultSummary: { kind: 'generated_image', truncated: false, file_id: 'file-1' } as never,
+ })]);
+ expect(collectPendingImageGenerations(retried, [block])).toEqual([]);
+ });
});
diff --git a/frontend/src/components/chat/GeneratedImages.tsx b/frontend/src/components/chat/GeneratedImages.tsx
index 92699df6..34856d77 100644
--- a/frontend/src/components/chat/GeneratedImages.tsx
+++ b/frontend/src/components/chat/GeneratedImages.tsx
@@ -94,7 +94,7 @@ function PendingImageCard({ pending }: { pending: PendingImageGeneration }) {
{label}
diff --git a/frontend/src/components/chat/generatedImageModel.ts b/frontend/src/components/chat/generatedImageModel.ts
index bf72f06f..04fbc791 100644
--- a/frontend/src/components/chat/generatedImageModel.ts
+++ b/frontend/src/components/chat/generatedImageModel.ts
@@ -57,33 +57,34 @@ export function collectPendingImageGenerations(
): PendingImageGeneration[] {
if (!run) return [];
const renderedFileIds = new Set(blocks.map(block => block.file_id));
+ const calls = run.steps.flatMap(step => step.toolCalls.filter(call => call.toolName === GENERATE_IMAGE_TOOL_NAME));
const pending: PendingImageGeneration[] = [];
- for (const step of run.steps) {
- for (const call of step.toolCalls) {
- if (call.toolName !== GENERATE_IMAGE_TOOL_NAME) continue;
- const summary = call.resultSummary as (Record | undefined);
- const fileId = typeof summary?.file_id === 'string' ? summary.file_id : null;
- let status: PendingImageStatus;
- if (call.status === 'running') {
- status = 'running';
- } else if (call.status === 'success') {
- if (!fileId || renderedFileIds.has(fileId)) continue;
- status = 'running';
- } else if (call.status === 'interrupted') {
- status = 'interrupted';
- } else {
- status = 'failed';
- }
- const aspectRatio = typeof call.arguments?.aspect_ratio === 'string' ? call.arguments.aspect_ratio : '1:1';
- pending.push({
- toolCallId: call.toolCallId,
- status,
- aspectRatio,
- startedAt: call.startedAt,
- completedAt: call.completedAt,
- errorCode: typeof summary?.error_code === 'string' ? summary.error_code : null,
- });
+ calls.forEach((call, index) => {
+ const summary = call.resultSummary as (Record | undefined);
+ const fileId = typeof summary?.file_id === 'string' ? summary.file_id : null;
+ let status: PendingImageStatus;
+ if (call.status === 'running') {
+ status = 'running';
+ } else if (call.status === 'success') {
+ if (!fileId || renderedFileIds.has(fileId)) return;
+ status = 'running';
+ } else if (index < calls.length - 1) {
+ // 失败或中断后又发起了生图(重试),由后一张卡片接替,不再保留失败卡片。
+ return;
+ } else if (call.status === 'interrupted') {
+ status = 'interrupted';
+ } else {
+ status = 'failed';
}
- }
+ const aspectRatio = typeof call.arguments?.aspect_ratio === 'string' ? call.arguments.aspect_ratio : '1:1';
+ pending.push({
+ toolCallId: call.toolCallId,
+ status,
+ aspectRatio,
+ startedAt: call.startedAt,
+ completedAt: call.completedAt,
+ errorCode: typeof summary?.error_code === 'string' ? summary.error_code : null,
+ });
+ });
return pending;
}