test_agui_adapter_fake_id.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. """Tests for handling LiteLLM/Chat Completions placeholder IDs.
  2. When using non-Responses API models (LiteLLM, Chat Completions), the SDK emits
  3. `item.id = "__fake_id__"` for all output items. This causes collisions when
  4. using item.id as a map key. These tests verify the fix: skip placeholder IDs
  5. and use call_id for correlation instead.
  6. """
  7. from types import SimpleNamespace
  8. import pytest
  9. from agents.models.fake_id import FAKE_RESPONSES_ID
  10. pytest.importorskip("ag_ui")
  11. from ag_ui.core import ToolCallArgsEvent
  12. from openai.types.responses import ResponseFunctionToolCall
  13. from openai.types.responses.response_function_call_arguments_delta_event import (
  14. ResponseFunctionCallArgumentsDeltaEvent,
  15. )
  16. from openai.types.responses.response_output_item_added_event import ResponseOutputItemAddedEvent
  17. from agency_swarm.ui.core.agui_adapter import AguiAdapter
  18. def make_raw_event(data):
  19. return SimpleNamespace(type="raw_response_event", data=data)
  20. class TestAguiAdapterFakeIdHandling:
  21. """Tests for agui_adapter handling of __fake_id__ placeholder."""
  22. def test_multiple_tool_calls_with_fake_id_are_tracked_separately(self):
  23. """Multiple tool calls sharing __fake_id__ should each be tracked by call_id."""
  24. adapter = AguiAdapter()
  25. run_id = "fake-id-run"
  26. # First tool call with __fake_id__
  27. tool1 = ResponseFunctionToolCall(
  28. arguments="{}",
  29. call_id="call_first_tool",
  30. name="tool_one",
  31. type="function_call",
  32. id=FAKE_RESPONSES_ID, # Same placeholder ID
  33. status="in_progress",
  34. )
  35. # Second tool call with same __fake_id__
  36. tool2 = ResponseFunctionToolCall(
  37. arguments="{}",
  38. call_id="call_second_tool",
  39. name="tool_two",
  40. type="function_call",
  41. id=FAKE_RESPONSES_ID, # Same placeholder ID
  42. status="in_progress",
  43. )
  44. # Register first tool
  45. adapter.openai_to_agui_events(
  46. make_raw_event(
  47. ResponseOutputItemAddedEvent(
  48. item=tool1,
  49. output_index=0,
  50. sequence_number=1,
  51. type="response.output_item.added",
  52. )
  53. ),
  54. run_id=run_id,
  55. )
  56. # Register second tool
  57. adapter.openai_to_agui_events(
  58. make_raw_event(
  59. ResponseOutputItemAddedEvent(
  60. item=tool2,
  61. output_index=1,
  62. sequence_number=2,
  63. type="response.output_item.added",
  64. )
  65. ),
  66. run_id=run_id,
  67. )
  68. # Send delta for first tool - should map to call_first_tool
  69. # Using call_id directly since item_id is the placeholder
  70. delta1_event = adapter.openai_to_agui_events(
  71. make_raw_event(
  72. ResponseFunctionCallArgumentsDeltaEvent(
  73. item_id=FAKE_RESPONSES_ID,
  74. delta='{"arg1": "value1"}',
  75. output_index=0, # First tool's output_index
  76. sequence_number=3,
  77. type="response.function_call_arguments.delta",
  78. )
  79. ),
  80. run_id=run_id,
  81. )
  82. # Send delta for second tool - should map to call_second_tool
  83. delta2_event = adapter.openai_to_agui_events(
  84. make_raw_event(
  85. ResponseFunctionCallArgumentsDeltaEvent(
  86. item_id=FAKE_RESPONSES_ID,
  87. delta='{"arg2": "value2"}',
  88. output_index=1, # Second tool's output_index
  89. sequence_number=4,
  90. type="response.function_call_arguments.delta",
  91. )
  92. ),
  93. run_id=run_id,
  94. )
  95. # Both deltas should be properly mapped to different call_ids
  96. assert isinstance(delta1_event, ToolCallArgsEvent)
  97. assert isinstance(delta2_event, ToolCallArgsEvent)
  98. assert delta1_event.tool_call_id == "call_first_tool"
  99. assert delta2_event.tool_call_id == "call_second_tool"
  100. def test_tool_call_with_real_id_still_works(self):
  101. """Tool calls with real item IDs should continue to work."""
  102. adapter = AguiAdapter()
  103. run_id = "real-id-run"
  104. tool = ResponseFunctionToolCall(
  105. arguments="{}",
  106. call_id="call_real",
  107. name="real_tool",
  108. type="function_call",
  109. id="real_item_id", # Real ID, not placeholder
  110. status="in_progress",
  111. )
  112. adapter.openai_to_agui_events(
  113. make_raw_event(
  114. ResponseOutputItemAddedEvent(
  115. item=tool,
  116. output_index=0,
  117. sequence_number=1,
  118. type="response.output_item.added",
  119. )
  120. ),
  121. run_id=run_id,
  122. )
  123. delta_event = adapter.openai_to_agui_events(
  124. make_raw_event(
  125. ResponseFunctionCallArgumentsDeltaEvent(
  126. item_id="real_item_id",
  127. delta='{"key": "value"}',
  128. output_index=0,
  129. sequence_number=2,
  130. type="response.function_call_arguments.delta",
  131. )
  132. ),
  133. run_id=run_id,
  134. )
  135. assert isinstance(delta_event, ToolCallArgsEvent)
  136. assert delta_event.tool_call_id == "call_real"