@samitouri / QOSami_Agents_Zero / commits / 117ae265
Fork 0

Agent Zero AI framework

Stream native response tool output

Forward incremental response function arguments through the canonical Agent Zero tool envelope so final Codex answers render progressively while preserving buffered execution for non-response tool calls. Cover partial DirtyJson rendering, completed envelope assembly, duplicate completion suppression, and the transport contract.

Alessandro committed Aug 27, 2026 at 07:27 UTC 117ae265f9bb78e9d230c6832ec4aee3395e1236
3 files changed +91 -6
helpers/litellm_transport.py
+26 -6
@@ -1166,6 +1166,7 @@ class ResponsesEventParser:
1166 self.function_calls: dict[str, dict[str, Any]] = {}
1167 self.output_index_keys: dict[str, str] = {}
1168 self.emitted_function_calls: set[str] = set()
1169 + self.streamed_response_calls: dict[str, str] = {}
1170 self.seen_response_delta = False
1171 self.seen_reasoning_delta = False
1172 self.completed_response: Any = None
@@ -1189,7 +1190,7 @@ class ResponsesEventParser:
1190 elif event_type == "response.output_item.added":
1191 self._remember_function_call(_get_value(event, "item"), event)
1192 elif event_type == "response.function_call_arguments.delta":
1192 - self._append_function_call_arguments(event)
1193 + response_delta = self._append_function_call_arguments(event)
1194 elif event_type == "response.function_call_arguments.done":
1195 response_delta = self._complete_function_call(event)
1196 elif event_type == "response.output_item.done":
@@ -1224,14 +1225,22 @@ class ResponsesEventParser:
1225 self.output_index_keys[str(output_index)] = key
1226 return key
1227
1227 - def _append_function_call_arguments(self, event: Any) -> None:
1228 + def _append_function_call_arguments(self, event: Any) -> str:
1229 key = self._event_key(event)
1230 if not key:
1230 - return
1231 + return ""
1232 current = self.function_calls.setdefault(key, {"type": "function_call"})
1232 - current["arguments"] = str(current.get("arguments") or "") + str(
1233 - _get_value(event, "delta") or ""
1234 - )
1233 + delta = str(_get_value(event, "delta") or "")
1234 + current["arguments"] = str(current.get("arguments") or "") + delta
1235 + if current.get("name") != "response":
1236 + return ""
1237 + if key not in self.streamed_response_calls:
1238 + self.streamed_response_calls[key] = str(current["arguments"])
1239 + return '{"tool_name":"response","tool_args":' + str(
1240 + current["arguments"]
1241 + )
1242 + self.streamed_response_calls[key] += delta
1243 + return delta
1244
1245 def _complete_function_call(self, event: Any) -> str:
1246 key = self._event_key(event)
@@ -1242,14 +1251,25 @@ class ResponsesEventParser:
1251 current["arguments"] = _get_value(event, "arguments")
1252 if _get_value(event, "name"):
1253 current["name"] = _get_value(event, "name")
1254 + if key in self.streamed_response_calls:
1255 + return self._finish_response_call(key, current)
1256 return self._emit_function_call(key, current)
1257
1258 def _complete_output_item(self, item: Any, event: Any) -> str:
1259 key = self._remember_function_call(item, event)
1260 if not key:
1261 return ""
1262 + if key in self.streamed_response_calls:
1263 + return self._finish_response_call(key, self.function_calls[key])
1264 return self._emit_function_call(key, self.function_calls[key])
1265
1266 + def _finish_response_call(self, key: str, item: Any) -> str:
1267 + streamed = self.streamed_response_calls.pop(key)
1268 + arguments = str(_get_value(item, "arguments") or "")
1269 + self.emitted_function_calls.add(key)
1270 + tail = arguments[len(streamed) :] if arguments.startswith(streamed) else ""
1271 + return tail + "}"
1272 +
1273 def _complete_response(self, event: Any) -> tuple[str, str]:
1274 self.completed_response = _get_value(event, "response")
1275 if self.seen_response_delta or self.emitted_function_calls:
helpers/litellm_transport.py.dox.md
+1
@@ -35,6 +35,7 @@
35 - Preserve Chat Completions tool calls from both non-streaming responses and streaming deltas as canonical `LLMResult` function-call items.
36 - Preserve provider usage and LiteLLM response cost for both transports only when the response or stream actually supplies them; do not synthesize unavailable provider accounting.
37 - Preserve Responses function calls collected from stream events when a terminal completed event omits them.
38 +- Stream native `response` function arguments through a canonical response-tool envelope while continuing to buffer other function calls until completion.
39 - Serialize synthesized Responses function-call JSON with literal Unicode so streamed raw-response logs preserve tool arguments.
40 - Preserve provider-state metadata when Responses API calls succeed, and fall back to local replay when provider state is unsupported.
41 - Keep prompt-cache markers only for providers that accept them.
tests/test_stream_tool_early_stop.py
+64
@@ -13,6 +13,7 @@ if str(PROJECT_ROOT) not in sys.path:
13 import models
14 from helpers import extract_tools
15 from helpers import litellm_transport
16 +from helpers.dirty_json import DirtyJson
17
18
19 @pytest.fixture(autouse=True)
@@ -1704,6 +1705,69 @@ def test_responses_stream_parser_accumulates_function_call_arguments():
1705 ) == {"reasoning_delta": "", "response_delta": ""}
1706
1707
1708 +def test_responses_stream_parser_streams_response_function_arguments():
1709 + parser = litellm_transport.ResponsesEventParser()
1710 +
1711 + parser.parse(
1712 + {
1713 + "type": "response.output_item.added",
1714 + "output_index": 0,
1715 + "item": {
1716 + "type": "function_call",
1717 + "id": "fc_1",
1718 + "name": "response",
1719 + "arguments": "",
1720 + },
1721 + }
1722 + )
1723 + chunks = [
1724 + parser.parse(
1725 + {
1726 + "type": "response.function_call_arguments.delta",
1727 + "item_id": "fc_1",
1728 + "delta": '{"text":"Hello',
1729 + }
1730 + )["response_delta"],
1731 + parser.parse(
1732 + {
1733 + "type": "response.function_call_arguments.delta",
1734 + "item_id": "fc_1",
1735 + "delta": ' world"}',
1736 + }
1737 + )["response_delta"],
1738 + parser.parse(
1739 + {
1740 + "type": "response.function_call_arguments.done",
1741 + "item_id": "fc_1",
1742 + "name": "response",
1743 + "arguments": '{"text":"Hello world"}',
1744 + }
1745 + )["response_delta"],
1746 + ]
1747 +
1748 + assert chunks[0] == '{"tool_name":"response","tool_args":{"text":"Hello'
1749 + assert DirtyJson.parse_string(chunks[0]) == {
1750 + "tool_name": "response",
1751 + "tool_args": {"text": "Hello"},
1752 + }
1753 + assert extract_tools.json_parse_dirty("".join(chunks)) == {
1754 + "tool_name": "response",
1755 + "tool_args": {"text": "Hello world"},
1756 + }
1757 + assert chunks[-1] == "}"
1758 + assert parser.parse(
1759 + {
1760 + "type": "response.output_item.done",
1761 + "item": {
1762 + "type": "function_call",
1763 + "id": "fc_1",
1764 + "name": "response",
1765 + "arguments": '{"text":"Hello world"}',
1766 + },
1767 + }
1768 + ) == {"reasoning_delta": "", "response_delta": ""}
1769 +
1770 +
1771 def test_responses_stream_parser_uses_completed_response_when_no_deltas_arrive():
1772 parser = litellm_transport.ResponsesEventParser()
1773