diff --git a/src/stores/aiChatChartBlocks.test.ts b/src/stores/aiChatChartBlocks.test.ts index 53d1fe71..e928c422 100644 --- a/src/stores/aiChatChartBlocks.test.ts +++ b/src/stores/aiChatChartBlocks.test.ts @@ -120,6 +120,28 @@ describe('aiChat chart block helpers', () => { assert.equal(nextBlocks[1]?.type, 'chart') }) + it('keeps render-only charts in tool call order when results arrive out of order', () => { + const blocks = [ + createRenderOnlyToolPendingBlock('render_chart', { title: '第一张图' }, 'call_chart_a'), + createRenderOnlyToolPendingBlock('render_chart', { title: '第二张图' }, 'call_chart_b'), + ].filter((block): block is NonNullable => block !== null) + + const afterSecondCompletes = replaceRenderOnlyToolPendingBlockWithCharts(blocks, 'render_chart', 'call_chart_b', [ + secondChart, + ]) + const afterFirstCompletes = replaceRenderOnlyToolPendingBlockWithCharts( + afterSecondCompletes, + 'render_chart', + 'call_chart_a', + [chart] + ) + const chartTitles = afterFirstCompletes + .filter((block): block is { type: 'chart'; chart: ChartPayload } => block.type === 'chart') + .map((block) => block.chart.spec.title) + + assert.deepEqual(chartTitles, ['Daily messages', 'Selected members']) + }) + it('does not append duplicate render-only charts already present in the message', () => { const reorderedSpecChart: ChartPayload = { ...chart, diff --git a/src/stores/aiChatChartBlocks.ts b/src/stores/aiChatChartBlocks.ts index dbbe9933..bfc83fd9 100644 --- a/src/stores/aiChatChartBlocks.ts +++ b/src/stores/aiChatChartBlocks.ts @@ -181,9 +181,26 @@ export function replaceRenderOnlyToolPendingBlockWithCharts( toolCallId: string | undefined, charts: ChartPayload[] ): Array { - const withoutPending = removeRenderOnlyToolPendingBlock(blocks, toolName, toolCallId) - const uniqueCharts = charts.filter((chart) => !hasDuplicateChart(withoutPending, chart)) - return [...withoutPending, ...toChartContentBlocks(uniqueCharts)] + const next = [...blocks] as Array + let pendingIndex = -1 + + for (let index = next.length - 1; index >= 0; index--) { + if (isMatchingPendingRenderOnlyToolBlock(next[index], toolName, toolCallId)) { + pendingIndex = index + break + } + } + + const duplicateBase = pendingIndex >= 0 ? next.filter((_block, index) => index !== pendingIndex) : next + const uniqueCharts = charts.filter((chart) => !hasDuplicateChart(duplicateBase, chart)) + const chartBlocks = toChartContentBlocks(uniqueCharts) + + if (pendingIndex >= 0) { + next.splice(pendingIndex, 1, ...chartBlocks) + return next + } + + return [...next, ...chartBlocks] } export function finishRenderOnlyToolResultBlocks(