Skip to content

Commit 6ad0120

Browse files
author
Murat Kaan Meral
committed
fix: fix nova completion id tracking
1 parent 30c0a5d commit 6ad0120

1 file changed

Lines changed: 42 additions & 12 deletions

File tree

  • src/strands/experimental/bidirectional_streaming/models

src/strands/experimental/bidirectional_streaming/models/novasonic.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ def __init__(
126126
# Background task and event queue
127127
self._response_task = None
128128
self._event_queue = None
129+
130+
# Track API-provided identifiers
131+
self._current_completion_id = None
132+
self._current_role = None
129133

130134
logger.debug("Nova Sonic bidirectional model initialized: %s", model_id)
131135

@@ -510,6 +514,28 @@ async def close(self) -> None:
510514

511515
def _convert_nova_event(self, nova_event: dict[str, any]) -> OutputEvent | None:
512516
"""Convert Nova Sonic events to TypedEvent format."""
517+
# Handle completion start - track completionId
518+
if "completionStart" in nova_event:
519+
completion_data = nova_event["completionStart"]
520+
self._current_completion_id = completion_data.get("completionId")
521+
logger.debug("Nova completion started: %s", self._current_completion_id)
522+
return None
523+
524+
# Handle completion end
525+
if "completionEnd" in nova_event:
526+
completion_data = nova_event["completionEnd"]
527+
completion_id = completion_data.get("completionId", self._current_completion_id)
528+
stop_reason = completion_data.get("stopReason", "END_TURN")
529+
530+
event = ResponseCompleteEvent(
531+
response_id=completion_id or str(uuid.uuid4()), # Fallback to UUID if missing
532+
stop_reason="interrupted" if stop_reason == "INTERRUPTED" else "complete"
533+
)
534+
535+
# Clear completion tracking
536+
self._current_completion_id = None
537+
return event
538+
513539
# Handle audio output
514540
if "audioOutput" in nova_event:
515541
# Audio is already base64 string from Nova Sonic
@@ -557,7 +583,7 @@ def _convert_nova_event(self, nova_event: dict[str, any]) -> OutputEvent | None:
557583
# Handle interruption
558584
elif nova_event.get("stopReason") == "INTERRUPTED":
559585
logger.debug("Nova interruption stop reason")
560-
return InterruptionEvent(reason="user_speech", response_id=None)
586+
return InterruptionEvent(reason="user_speech")
561587

562588
# Handle usage events - convert to multimodal usage format
563589
elif "usageEvent" in nova_event:
@@ -571,22 +597,26 @@ def _convert_nova_event(self, nova_event: dict[str, any]) -> OutputEvent | None:
571597
total_tokens=usage_data.get("totalTokens", total_input + total_output)
572598
)
573599

574-
# Handle content start events (track role)
600+
# Handle content start events (track role and emit response start)
575601
elif "contentStart" in nova_event:
576-
role = nova_event["contentStart"].get("role", "unknown")
602+
content_data = nova_event["contentStart"]
603+
role = content_data.get("role", "unknown")
577604
# Store role for subsequent text output events
578605
self._current_role = role
579-
# Emit response start event
580-
return ResponseStartEvent(response_id=str(uuid.uuid4()))
581-
582-
# Handle content stop events
583-
elif "contentStop" in nova_event:
584-
stop_reason = nova_event["contentStop"].get("stopReason", "complete")
585-
return ResponseCompleteEvent(
586-
response_id=str(uuid.uuid4()),
587-
stop_reason="interrupted" if stop_reason == "INTERRUPTED" else "complete"
606+
607+
# Emit response start event using API-provided completionId
608+
# completionId should already be tracked from completionStart event
609+
return ResponseStartEvent(
610+
response_id=self._current_completion_id or str(uuid.uuid4()) # Fallback to UUID if missing
588611
)
589612

613+
# Handle content end events
614+
elif "contentEnd" in nova_event:
615+
# contentEnd doesn't signal response completion in Nova Sonic
616+
# Multiple content blocks can exist in a single response
617+
# Only completionEnd signals the actual response completion
618+
return None
619+
590620
# Handle other events
591621
else:
592622
return None

0 commit comments

Comments
 (0)