Skip to content

Commit f75737f

Browse files
committed
test for duplicate chunk ids, reorder chunk id increment to prevent race with yield
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
1 parent a23ccbb commit f75737f

2 files changed

Lines changed: 15 additions & 10 deletions

File tree

src/app/endpoints/streaming_query.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -771,16 +771,17 @@ async def response_generator( # pylint: disable=too-many-branches,too-many-stat
771771

772772
# Content part started - emit an empty token to kick off UI streaming
773773
if event_type == "response.content_part.added":
774+
event_id = chunk_id
775+
chunk_id += 1
776+
turn_summary.next_chunk_id = chunk_id
774777
yield stream_event(
775778
{
776-
"id": chunk_id,
779+
"id": event_id,
777780
"token": "",
778781
},
779782
LLM_TOKEN_EVENT,
780783
media_type,
781784
)
782-
chunk_id += 1
783-
turn_summary.next_chunk_id = chunk_id
784785

785786
# Store MCP call item info for later lookup when arguments.done event occurs
786787
elif event_type == "response.output_item.added":
@@ -797,16 +798,17 @@ async def response_generator( # pylint: disable=too-many-branches,too-many-stat
797798
delta_chunk = cast(TextDeltaChunk, chunk)
798799
text_parts.append(delta_chunk.delta)
799800
turn_summary.partial_tokens.append(delta_chunk.delta)
801+
event_id = chunk_id
802+
chunk_id += 1
803+
turn_summary.next_chunk_id = chunk_id
800804
yield stream_event(
801805
{
802-
"id": chunk_id,
806+
"id": event_id,
803807
"token": delta_chunk.delta,
804808
},
805809
LLM_TOKEN_EVENT,
806810
media_type,
807811
)
808-
chunk_id += 1
809-
turn_summary.next_chunk_id = chunk_id
810812

811813
# Final text of the output (capture, but emit at response.completed)
812814
elif event_type == "response.output_text.done":
@@ -886,16 +888,17 @@ async def response_generator( # pylint: disable=too-many-branches,too-many-stat
886888
# (LCORE-1572), so the persisted turn keeps non-text output items
887889
# rather than being flattened to the response text.
888890
turn_summary.output_items = list(latest_response_object.output or [])
891+
event_id = chunk_id
892+
chunk_id += 1
893+
turn_summary.next_chunk_id = chunk_id
889894
yield stream_event(
890895
{
891-
"id": chunk_id,
896+
"id": event_id,
892897
"token": turn_summary.llm_response,
893898
},
894899
LLM_TURN_COMPLETE_EVENT,
895900
media_type,
896901
)
897-
chunk_id += 1
898-
turn_summary.next_chunk_id = chunk_id
899902

900903
# Incomplete or failed response - emit error
901904
elif event_type in ("response.incomplete", "response.failed"):

tests/unit/utils/agents/test_streaming.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,9 +1052,11 @@ async def __aexit__(self, *_args: object) -> None:
10521052
and json.loads(e.removeprefix("data: ").strip())["event"] == "token"
10531053
]
10541054
chunk_ids = [t["data"]["id"] for t in token_events]
1055+
num_chunks = len(chunk_ids)
10551056
assert chunk_ids == sorted(chunk_ids), "chunk_ids must be monotonically ordered"
10561057
assert all(cid >= 0 for cid in chunk_ids), "all chunk_ids must be non-negative"
1057-
assert chunk_ids[-1] == len(chunk_ids) - 1
1058+
assert num_chunks == len(set(chunk_ids)), "chunk_ids must not contain duplicates"
1059+
assert chunk_ids[-1] == num_chunks - 1
10581060

10591061
@pytest.mark.asyncio
10601062
async def test_interrupt_with_no_tokens_uses_zero_chunk_id(

0 commit comments

Comments
 (0)