Skip to content

Commit 5475f4d

Browse files
Fix Claude Sonnet 4 streaming detection issue (#400)
Replace fragile usage_metadata-based logic with robust streaming detection that checks multiple explicit streaming indicators. **Problem:** The original logic relied on `not adk_event.usage_metadata` to determine if an event should be processed as streaming. This was fragile because Claude models can include usage_metadata even in streaming chunks, causing responses to disappear. **Solution:** Implement comprehensive streaming detection that checks: - `partial` attribute (explicitly marked as partial) - `turn_complete` attribute (live streaming completion status) - `is_final_response()` method (final response indicator) - `finish_reason` attribute (fallback for content without finish reason) This ensures all streaming content is captured regardless of usage_metadata presence, fixing compatibility with Claude Sonnet 4 and other models. **Testing:** ✅ All 277 tests pass ✅ Streaming detection works across different model providers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent ccf517f commit 5475f4d

File tree

1 file changed

+13
-4
lines changed
  • typescript-sdk/integrations/adk-middleware/python/src/ag_ui_adk

1 file changed

+13
-4
lines changed

typescript-sdk/integrations/adk-middleware/python/src/ag_ui_adk/adk_agent.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -916,19 +916,28 @@ async def _run_adk_in_background(
916916
final_response = adk_event.is_final_response()
917917
has_content = adk_event.content and hasattr(adk_event.content, 'parts') and adk_event.content.parts
918918

919-
if not final_response or (not adk_event.usage_metadata and has_content):
920-
# Translate and emit events
919+
# Check if this is a streaming chunk that needs regular processing
920+
is_streaming_chunk = (
921+
getattr(adk_event, 'partial', False) or # Explicitly marked as partial
922+
(not getattr(adk_event, 'turn_complete', True)) or # Live streaming not complete
923+
(not final_response) # Not marked as final by is_final_response()
924+
)
925+
926+
# Process as streaming if it's a chunk OR if it has content but no finish_reason
927+
# This ensures we capture all content, regardless of usage_metadata presence
928+
if is_streaming_chunk or (has_content and not getattr(adk_event, 'finish_reason', None)):
929+
# Regular translation path
921930
async for ag_ui_event in event_translator.translate(
922931
adk_event,
923932
input.thread_id,
924933
input.run_id
925934
):
926-
935+
927936
logger.debug(f"Emitting event to queue: {type(ag_ui_event).__name__} (thread {input.thread_id}, queue size before: {event_queue.qsize()})")
928937
await event_queue.put(ag_ui_event)
929938
logger.debug(f"Event queued: {type(ag_ui_event).__name__} (thread {input.thread_id}, queue size after: {event_queue.qsize()})")
930939
else:
931-
# LongRunning Tool events are usually emmitted in final response
940+
# LongRunning Tool events are usually emmitted in final response
932941
async for ag_ui_event in event_translator.translate_lro_function_calls(
933942
adk_event
934943
):

0 commit comments

Comments
 (0)