Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions python/packages/devui/agent_framework_devui/_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1340,9 +1340,9 @@ async def _map_function_call_content(
"""
events: list[ResponseFunctionCallArgumentsDeltaEvent | ResponseOutputItemAddedEvent] = []

# CASE 1: New function call (has call_id and name)
# This is the first event that establishes the function call
if content.call_id and content.name:
# CASE 1: New function call (has a call_id and name not seen in an earlier chunk)
# Streaming providers may repeat call metadata with every argument chunk.
if content.call_id and content.name and content.call_id not in context["active_function_calls"]:
# Use call_id as item_id (simpler, and call_id uniquely identifies the call)
item_id = content.call_id

Expand Down
30 changes: 30 additions & 0 deletions python/packages/devui/tests/devui/test_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,36 @@ async def test_function_call_mapping(mapper: MessageMapper, test_request: AgentF
assert "TestCity" in full_json


async def test_streaming_function_call_mapping_adds_item_once(
mapper: MessageMapper, test_request: AgentFrameworkRequest
) -> None:
"""Test repeated function metadata does not add duplicate output items."""
first_update = create_test_agent_update([
Content.from_function_call(
call_id="call_123",
name="get_weather",
arguments='{"location":',
)
])
second_update = create_test_agent_update([
Content.from_function_call(
call_id="call_123",
name="get_weather",
arguments='"Seattle"}',
)
])

events = [
*await mapper.convert_event(first_update, test_request),
*await mapper.convert_event(second_update, test_request),
]

added_events = [event for event in events if event.type == "response.output_item.added"]
assert len(added_events) == 1
delta_events = [event for event in events if event.type == "response.function_call_arguments.delta"]
assert "".join(event.delta for event in delta_events) == '{"location":"Seattle"}'


async def test_function_result_content_with_string_result(
mapper: MessageMapper, test_request: AgentFrameworkRequest
) -> None:
Expand Down
Loading