fix(agent/gemini-cloudcode): seed delta defaults for reasoning-only stream chunks

_make_stream_chunk built delta_kwargs with only `role`, so a reasoning-only
chunk produced a SimpleNamespace without a `.content` attribute. Downstream
consumers that read `delta.content` then raised AttributeError on Gemini 2.5
Flash, where the thinking delta arrives before any content delta.

Seed `content`, `tool_calls`, `reasoning`, and `reasoning_content` as None
up front, matching the pattern already used in gemini_native_adapter.py.
Key-present arguments still override the defaults.

Fixes #24974
References: Related open PR #24984 (luyao618) applies the same 1-line fix; this PR adds a regression test that #24984 omits
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
EthanGuo-coder 2026-05-14 08:03:50 -07:00 committed by Teknium
parent 72b5dd8658
commit 26933c2f59
2 changed files with 36 additions and 1 deletions

View file

@ -450,7 +450,13 @@ def _make_stream_chunk(
finish_reason: Optional[str] = None,
reasoning: str = "",
) -> _GeminiStreamChunk:
delta_kwargs: Dict[str, Any] = {"role": "assistant"}
delta_kwargs: Dict[str, Any] = {
"role": "assistant",
"content": None,
"tool_calls": None,
"reasoning": None,
"reasoning_content": None,
}
if content:
delta_kwargs["content"] = content
if tool_call_delta is not None:

View file

@ -913,6 +913,35 @@ class TestTranslateStreamEvent:
assert chunks[-1].choices[0].finish_reason == "tool_calls"
class TestMakeStreamChunk:
def test_reasoning_only_chunk_has_content_none(self):
from agent.gemini_cloudcode_adapter import _make_stream_chunk
chunk = _make_stream_chunk(model="m", reasoning="think")
delta = chunk.choices[0].delta
assert delta.content is None
assert delta.reasoning == "think"
def test_content_only_chunk_has_reasoning_none(self):
from agent.gemini_cloudcode_adapter import _make_stream_chunk
chunk = _make_stream_chunk(model="m", content="hello")
delta = chunk.choices[0].delta
assert delta.content == "hello"
assert delta.reasoning is None
assert delta.tool_calls is None
def test_finish_only_chunk_has_all_fields_none(self):
from agent.gemini_cloudcode_adapter import _make_stream_chunk
chunk = _make_stream_chunk(model="m", finish_reason="stop")
delta = chunk.choices[0].delta
assert delta.content is None
assert delta.reasoning is None
assert delta.tool_calls is None
assert chunk.choices[0].finish_reason == "stop"
class TestGeminiCloudCodeClient:
def test_client_exposes_openai_interface(self):
from agent.gemini_cloudcode_adapter import GeminiCloudCodeClient