From cc82345bc0453c134b82ad888606d75b2eb375dc Mon Sep 17 00:00:00 2001 From: Yahya Date: Mon, 8 Jun 2026 10:27:46 +0200 Subject: [PATCH] fix(chatcompletions): only repair args that look like truncated JSON The previous repair pass rewrote any non-parseable arguments string to "{}", which clobbered legitimate accumulator contents in test_stream_response_yields_events_for_tool_call (concatenated raw deltas like "arg1arg2"). Narrow the guard: only when the stripped buffer starts with "{" or "[" *and* fails to parse do we replace it with "{}". Everything else is left untouched. --- .../models/chatcompletions/message_builder.py | 14 +++++++++----- .../sdk/agents/models/openai_chatcompletions.py | 10 +++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/cai/sdk/agents/models/chatcompletions/message_builder.py b/src/cai/sdk/agents/models/chatcompletions/message_builder.py index a1f8968f..c2631f0c 100644 --- a/src/cai/sdk/agents/models/chatcompletions/message_builder.py +++ b/src/cai/sdk/agents/models/chatcompletions/message_builder.py @@ -427,11 +427,15 @@ class Converter: else: # Truncated/streamed-then-cut function call args (e.g. "{") # are not valid JSON and the upstream proxy rejects the whole - # request with HTTP 400, wedging the conversation. - try: - json.loads(arguments) - except (TypeError, ValueError): - arguments = "{}" + # request with HTTP 400, wedging the conversation. Only repair + # half-finished JSON object/array buffers; leave anything else + # (raw concatenated deltas, provider-specific blobs) alone. + stripped = arguments.lstrip() if isinstance(arguments, str) else "" + if stripped and stripped[0] in "{[": + try: + json.loads(arguments) + except (TypeError, ValueError): + arguments = "{}" tool_calls_param.append( ChatCompletionMessageToolCallParam( id=tc.get("id", "")[:40], diff --git a/src/cai/sdk/agents/models/openai_chatcompletions.py b/src/cai/sdk/agents/models/openai_chatcompletions.py index 7156de4d..ff91cbf6 100644 --- a/src/cai/sdk/agents/models/openai_chatcompletions.py +++ b/src/cai/sdk/agents/models/openai_chatcompletions.py @@ -2678,12 +2678,16 @@ class OpenAIChatCompletionsModel(Model): # Repair partially-streamed function calls before emission so a # truncated args string like "{" cannot poison conversation history - # and trigger HTTP 400 on the next request. + # and trigger HTTP 400 on the next request. Only rewrite when the + # buffer is clearly a half-finished JSON object/array; leave other + # accumulator contents (raw concatenated deltas, provider quirks) + # untouched so the rest of the pipeline can decide. for _fc in state.function_calls.values(): _args = _fc.arguments or "" - if not _args.strip(): + _stripped = _args.lstrip() + if not _stripped: _fc.arguments = "{}" - else: + elif _stripped[0] in "{[": try: json.loads(_args) except (TypeError, ValueError):