fix: handle missing functionResponse.id during SSE streams#86
Conversation
Under some circumstances (such as when using ADK's `AgentTool` or `SequentialAgent`), the `functionResponse.id` field may be missing during real-time SSE streaming. This prevents the frontend from correctly associating the response with its corresponding tool invocation, resulting in the response not rendering in the UI until a full history fetch occurs. This commit updates `applyEventToState` in `chatCache.ts` to fallback to searching the existing `toolInvocations` for the most recent unresponded tool call with the matching `name` when the `id` is missing. This ensures the live SSE updates are immediately correctly rendered. Co-authored-by: MrOrz <108608+MrOrz@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Code Review
This pull request primarily focuses on code formatting, import cleanups, and semicolon removals across various frontend and backend files. Additionally, it introduces a fallback mechanism in src/lib/chatCache.ts to resolve missing functionResponse.ids during SSE by matching the name of the last unanswered tool invocation. Feedback on this change suggests capturing part.functionResponse.name in a local constant to avoid using the non-null assertion operator !, which is safer and more idiomatic in TypeScript.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (!key && part.functionResponse.name) { | ||
| const matchingIds = Object.keys(toolInvocations).filter( | ||
| (id) => | ||
| toolInvocations[id].name === part.functionResponse!.name && | ||
| !toolInvocations[id].resp, | ||
| ) | ||
| if (matchingIds.length > 0) { | ||
| // Pick the last one (most recent) | ||
| key = matchingIds[matchingIds.length - 1] | ||
| } | ||
| } |
There was a problem hiding this comment.
Instead of using the non-null assertion operator ! inside the .filter() callback, capture part.functionResponse.name in a local constant before the filter. This is safer and more idiomatic in TypeScript, as it avoids potential issues with lexical scope narrowing.
if (!key && part.functionResponse.name) {
const name = part.functionResponse.name
const matchingIds = Object.keys(toolInvocations).filter(
(id) =>
toolInvocations[id].name === name &&
!toolInvocations[id].resp,
)
if (matchingIds.length > 0) {
// Pick the last one (most recent)
key = matchingIds[matchingIds.length - 1]
}
}
Under some circumstances (such as when using ADK's
AgentToolorSequentialAgentor when sending via a separate SSE request), thefunctionResponse.idfield may be missing during real-time SSE streaming. This causes theapplyEventToStatelogic insrc/lib/chatCache.tsto drop the response because it tries to look uptoolInvocationsusing thenameas a fallback, which is undefined (since the original call was saved using its uniqueid).As a result, the tool response does not render in the UI during streaming. However, upon a page refresh, the full session history is fetched, which correctly includes the
idin thefunctionResponse, making it appear.This PR fixes the issue by updating
applyEventToState. Ifpart.functionResponse.idis missing butpart.functionResponse.nameis present, it will iterate throughtoolInvocationsto find the most recent matching tool call by itsnamethat hasn't received arespyet, and use thatidas the key. This ensures the real-time SSE updatestoolInvocationscorrectly and the UI drawer immediately shows the results.PR created automatically by Jules for task 7885769929636350666 started by @MrOrz