Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/strands/event_loop/streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ async def process_stream(chunks: AsyncIterable[StreamEvent]) -> AsyncGenerator[T
"text": "",
"current_tool_use": {},
"reasoningText": "",
"signature": "",
"citationsContent": [],
}
state["content"] = state["message"]["content"]
Expand Down
4 changes: 4 additions & 0 deletions src/strands/types/_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ def __init__(
"""
super().__init__({"stop": (stop_reason, message, usage, metrics)})

@property
def message(self) -> Message:
return cast(Message, self["stop"][1])

@property
@override
def is_callback_event(self) -> bool:
Expand Down
77 changes: 76 additions & 1 deletion tests/strands/event_loop/test_streaming.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import unittest.mock
from typing import cast

import pytest

import strands
import strands.event_loop
from strands.types._events import TypedEvent
from strands.types._events import ModelStopReason, TypedEvent
from strands.types.streaming import (
ContentBlockDeltaEvent,
ContentBlockStartEvent,
Expand Down Expand Up @@ -565,6 +566,80 @@ async def test_process_stream(response, exp_events, agenerator, alist):
assert non_typed_events == []


@pytest.mark.asyncio
async def test_process_stream_with_no_signature(agenerator, alist):
response = [
{"messageStart": {"role": "assistant"}},
{
"contentBlockDelta": {
"delta": {"reasoningContent": {"text": 'User asks: "Reason about 2+2" so I will do that'}},
"contentBlockIndex": 0,
}
},
{"contentBlockDelta": {"delta": {"reasoningContent": {"text": "."}}, "contentBlockIndex": 0}},
{"contentBlockStop": {"contentBlockIndex": 0}},
{
"contentBlockDelta": {
"delta": {"text": "Sure! Let’s do it"},
"contentBlockIndex": 1,
}
},
{"contentBlockStop": {"contentBlockIndex": 1}},
{"messageStop": {"stopReason": "end_turn"}},
{
"metadata": {
"usage": {"inputTokens": 112, "outputTokens": 764, "totalTokens": 876},
"metrics": {"latencyMs": 2970},
}
},
]

stream = strands.event_loop.streaming.process_stream(agenerator(response))

last_event = cast(ModelStopReason, (await alist(stream))[-1])

assert "signature" not in last_event.message["content"][0]["reasoningContent"]["reasoningText"]
assert last_event.message["content"][1]["text"] == "Sure! Let’s do it"


@pytest.mark.asyncio
async def test_process_stream_with_signature(agenerator, alist):
response = [
{"messageStart": {"role": "assistant"}},
{
"contentBlockDelta": {
"delta": {"reasoningContent": {"text": 'User asks: "Reason about 2+2" so I will do that'}},
"contentBlockIndex": 0,
}
},
{"contentBlockDelta": {"delta": {"reasoningContent": {"text": "."}}, "contentBlockIndex": 0}},
{"contentBlockDelta": {"delta": {"reasoningContent": {"signature": "test-"}}, "contentBlockIndex": 0}},
{"contentBlockDelta": {"delta": {"reasoningContent": {"signature": "signature"}}, "contentBlockIndex": 0}},
{"contentBlockStop": {"contentBlockIndex": 0}},
{
"contentBlockDelta": {
"delta": {"text": "Sure! Let’s do it"},
"contentBlockIndex": 1,
}
},
{"contentBlockStop": {"contentBlockIndex": 1}},
{"messageStop": {"stopReason": "end_turn"}},
{
"metadata": {
"usage": {"inputTokens": 112, "outputTokens": 764, "totalTokens": 876},
"metrics": {"latencyMs": 2970},
}
},
]

stream = strands.event_loop.streaming.process_stream(agenerator(response))

last_event = cast(ModelStopReason, (await alist(stream))[-1])

assert last_event.message["content"][0]["reasoningContent"]["reasoningText"]["signature"] == "test-signature"
assert last_event.message["content"][1]["text"] == "Sure! Let’s do it"


@pytest.mark.asyncio
async def test_stream_messages(agenerator, alist):
mock_model = unittest.mock.MagicMock()
Expand Down
Loading