Repository navigation
fix: agent output render issue - #505
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds live agent sub-tool-call progress tracking: stream processing now returns structured Changes
Sequence Diagram(s)sequenceDiagram
participant Agent as Agent Stream Processor
participant Store as AI Store
participant UI as ToolPartRenderer
Agent->>Agent: Process streamed UIMessageChunks (tool events)
Agent->>Store: updateAgentProgress(parentToolCallId, toolCalls)
Store->>UI: state change (agentProgress updated)
UI->>UI: Render AgentProgressRenderer (live tool calls)
Agent->>Agent: Stream completes -> assemble finalOutput
Agent->>Store: clearAgentProgress(parentToolCallId)
UI->>UI: Render final AgentProgressRenderer with agentToolCalls
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/ai-core/src/agents/AgentUtils.ts (1)
151-161: Minor: Abort signal not checked duringawait agentResult.text.If the abort signal fires while awaiting
agentResult.textat line 156, the abort won't be immediately respected since the promise doesn't accept an abort signal. The current behavior is acceptable for most use cases, but for strict cancellation semantics, consider wrapping withPromise.raceagainst an abort promise.♻️ Optional: Race against abort signal
throwIfAborted(); - const finalOutput = await agentResult.text; + const finalOutput = await Promise.race([ + agentResult.text, + new Promise<never>((_, reject) => { + if (abortSignal?.aborted) { + reject(new ToolAbortError(TOOL_CALL_CANCELLED)); + } + abortSignal?.addEventListener('abort', () => { + reject(new ToolAbortError(TOOL_CALL_CANCELLED)); + }, { once: true }); + }), + ]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/agents/AgentUtils.ts` around lines 151 - 161, The await on agentResult.text can block cancellation; wrap waiting for agentResult.text in a Promise.race with an abort promise so the abort signal is respected: create an abortPromise that rejects (or resolves to a sentinel) when throwIfAborted() would trigger (or listen to the same AbortSignal used elsewhere), then replace `const finalOutput = await agentResult.text;` with `const finalOutput = await Promise.race([agentResult.text, abortPromise]);` and handle the abort case by calling throwIfAborted() (or rethrowing) so cancellation flows through; keep the existing finally cleanup (store.getState().ai.clearAgentProgress(parentToolCallId)) and preserve returning `agentToolCalls: Array.from(toolCallMap.values())`.packages/ai-core/src/components/ToolPartRenderer.tsx (1)
188-216: Consider consolidating redundant store reads.
liveProgressis read from the store at line 188 inAgentProgressSection, and then read again at line 101 insideAgentProgressRenderer. SinceAgentProgressRendereralready reads live progress internally, the check at line 188 could be simplified by always renderingAgentProgressRendererand letting it handle the live vs. fallback logic entirely.♻️ Potential simplification
const AgentProgressSection: React.FC<{ toolCallId: string; output: unknown; input: unknown; state: | 'input-streaming' | 'input-available' | 'output-available' | 'output-error'; errorText?: string; }> = ({toolCallId, output, input, state, errorText}) => { - const liveProgress = useStoreWithAi((s) => s.ai.agentProgress[toolCallId]); - const agentOutput = output as { agentToolCalls?: Array<{ toolCallId: string; toolName: string; output?: unknown; errorText?: string; state: 'pending' | 'success' | 'error'; }>; finalOutput?: string; }; - const hasFinishedAgentProgress = - agentOutput?.agentToolCalls && agentOutput.agentToolCalls.length > 0; - const reasoning = input instanceof Object && 'reasoning' in input ? (input.reasoning as string) : undefined; - if (liveProgress && liveProgress.length > 0) { + // AgentProgressRenderer handles live progress internally + if (agentOutput?.agentToolCalls && agentOutput.agentToolCalls.length > 0) { return ( <AgentProgressRenderer toolCallId={toolCallId} - agentToolCalls={[]} + agentToolCalls={agentOutput.agentToolCalls} + finalOutput={agentOutput.finalOutput} reasoning={reasoning} /> ); } - if (hasFinishedAgentProgress) { - return ( - <AgentProgressRenderer - toolCallId={toolCallId} - agentToolCalls={agentOutput.agentToolCalls!} - finalOutput={agentOutput.finalOutput} - reasoning={reasoning} - /> - ); - } - return ( <ToolResult toolCallId={toolCallId}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/components/ToolPartRenderer.tsx` around lines 188 - 216, Currently ToolPartRenderer reads liveProgress via useStoreWithAi (liveProgress) and conditionally returns an AgentProgressRenderer when liveProgress exists; since AgentProgressRenderer already reads live progress itself, remove the redundant store read and the early-return branch: always render <AgentProgressRenderer toolCallId={toolCallId} agentToolCalls={agentOutput?.agentToolCalls ?? []} reasoning={reasoning} /> (or equivalent) so AgentProgressRenderer controls live vs. fallback display; delete the const liveProgress = ... and the if (liveProgress && liveProgress.length > 0) return ... block and ensure agentOutput and reasoning are still computed and passed through.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/ai-core/src/agents/AgentUtils.ts`:
- Around line 151-161: The await on agentResult.text can block cancellation;
wrap waiting for agentResult.text in a Promise.race with an abort promise so the
abort signal is respected: create an abortPromise that rejects (or resolves to a
sentinel) when throwIfAborted() would trigger (or listen to the same AbortSignal
used elsewhere), then replace `const finalOutput = await agentResult.text;` with
`const finalOutput = await Promise.race([agentResult.text, abortPromise]);` and
handle the abort case by calling throwIfAborted() (or rethrowing) so
cancellation flows through; keep the existing finally cleanup
(store.getState().ai.clearAgentProgress(parentToolCallId)) and preserve
returning `agentToolCalls: Array.from(toolCallMap.values())`.
In `@packages/ai-core/src/components/ToolPartRenderer.tsx`:
- Around line 188-216: Currently ToolPartRenderer reads liveProgress via
useStoreWithAi (liveProgress) and conditionally returns an AgentProgressRenderer
when liveProgress exists; since AgentProgressRenderer already reads live
progress itself, remove the redundant store read and the early-return branch:
always render <AgentProgressRenderer toolCallId={toolCallId}
agentToolCalls={agentOutput?.agentToolCalls ?? []} reasoning={reasoning} /> (or
equivalent) so AgentProgressRenderer controls live vs. fallback display; delete
the const liveProgress = ... and the if (liveProgress && liveProgress.length >
0) return ... block and ensure agentOutput and reasoning are still computed and
passed through.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eae43361-c52d-493b-b2b2-61ff959eb897
📒 Files selected for processing (7)
examples/ai-agent/src/agents/WeatherAgent.tspackages/ai-core/src/AiSlice.tspackages/ai-core/src/agents/AgentUtils.tspackages/ai-core/src/components/ToolPartRenderer.tsxpackages/ai-core/src/index.tspackages/ai-core/src/types.tspackages/ai/src/index.ts

after removing openassistant dependency and migrating tools to AI SDK standard tool, the tools of subagent can not be rendered in the main agent.
Summary by CodeRabbit