From 8a8555d591fd7b984cda7cf7c724f0987acada4c Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Wed, 3 Dec 2025 18:13:56 +0000 Subject: [PATCH] Optimize task tool summary recompute on version changes --- packages/ui/src/components/tool-call.tsx | 5 ++ .../components/tool-call/renderers/task.tsx | 63 +++++++------------ packages/ui/src/components/tool-call/types.ts | 2 + 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/packages/ui/src/components/tool-call.tsx b/packages/ui/src/components/tool-call.tsx index 0fbaf910..f23ff9ee 100644 --- a/packages/ui/src/components/tool-call.tsx +++ b/packages/ui/src/components/tool-call.tsx @@ -491,10 +491,15 @@ export default function ToolCall(props: ToolCallProps) { ) } + const messageVersionAccessor = createMemo(() => props.messageVersion) + const partVersionAccessor = createMemo(() => props.partVersion) + const rendererContext: ToolRendererContext = { toolCall: toolCallMemo, toolState, toolName, + messageVersion: messageVersionAccessor, + partVersion: partVersionAccessor, renderMarkdown: renderMarkdownContent, renderDiff: renderDiffContent, } diff --git a/packages/ui/src/components/tool-call/renderers/task.tsx b/packages/ui/src/components/tool-call/renderers/task.tsx index 25f92fbf..76df18f5 100644 --- a/packages/ui/src/components/tool-call/renderers/task.tsx +++ b/packages/ui/src/components/tool-call/renderers/task.tsx @@ -1,5 +1,4 @@ -import { For } from "solid-js" -import type { ToolState } from "@opencode-ai/sdk" +import { For, createMemo } from "solid-js" import type { ToolRenderer } from "../types" import { getRelativePath, getToolIcon, getToolName, readToolStatePayload } from "../utils" @@ -9,39 +8,6 @@ interface TaskSummaryItem { input: Record } -const taskSummaryCache = new Map() - -function normalizeTaskSummary(state?: ToolState, toolCallId?: string): TaskSummaryItem[] { - if (!state) return [] - const { metadata } = readToolStatePayload(state) - const rawSummary = Array.isArray((metadata as any).summary) ? ((metadata as any).summary as any[]) : [] - if (rawSummary.length === 0) { - if (toolCallId) taskSummaryCache.delete(toolCallId) - return [] - } - - const signature = JSON.stringify(rawSummary) - if (toolCallId) { - const cached = taskSummaryCache.get(toolCallId) - if (cached && cached.signature === signature) { - return cached.items - } - } - - const normalized: TaskSummaryItem[] = rawSummary.map((entry, index) => { - const tool = typeof entry?.tool === "string" ? (entry.tool as string) : "unknown" - const input = typeof (entry as any)?.state?.input === "object" && entry.state?.input ? entry.state.input : {} - const id = typeof entry?.id === "string" && entry.id.length > 0 ? entry.id : `${tool}-${index}` - return { id, tool, input } - }) - - if (toolCallId) { - taskSummaryCache.set(toolCallId, { signature, items: normalized }) - } - - return normalized -} - function describeTaskItem(item: TaskSummaryItem): string { const input = item.input || {} switch (item.tool) { @@ -74,17 +40,32 @@ export const taskRenderer: ToolRenderer = { } return base }, - renderBody({ toolState, toolCall }) { - const state = toolState() - if (!state) return null + renderBody({ toolState, toolCall, messageVersion, partVersion }) { + const items = createMemo(() => { + // Track the reactive change points so we only recompute when the part/message changes + messageVersion?.() + partVersion?.() - const items = normalizeTaskSummary(state, toolCall().id || "__unknown__") - if (items.length === 0) return null + const state = toolState() + if (!state) return [] + + const { metadata } = readToolStatePayload(state) + const summary = Array.isArray((metadata as any).summary) ? ((metadata as any).summary as any[]) : [] + + return summary.map((entry, index) => { + const tool = typeof entry?.tool === "string" ? (entry.tool as string) : "unknown" + const input = typeof (entry as any)?.state?.input === "object" && entry.state?.input ? entry.state.input : {} + const id = typeof entry?.id === "string" && entry.id.length > 0 ? entry.id : `${tool}-${index}` + return { id, tool, input } + }) + }) + + if (items().length === 0) return null return (
- + {(item) => { const icon = getToolIcon(item.tool) const description = describeTaskItem(item) diff --git a/packages/ui/src/components/tool-call/types.ts b/packages/ui/src/components/tool-call/types.ts index 45de9bd0..f0858628 100644 --- a/packages/ui/src/components/tool-call/types.ts +++ b/packages/ui/src/components/tool-call/types.ts @@ -25,6 +25,8 @@ export interface ToolRendererContext { toolCall: Accessor toolState: Accessor toolName: Accessor + messageVersion?: Accessor + partVersion?: Accessor renderMarkdown(options: MarkdownRenderOptions): JSXElement | null renderDiff(payload: DiffPayload, options?: DiffRenderOptions): JSXElement | null }