Summary
_make_stream_chunk() in agent/gemini_cloudcode_adapter.py builds the streamed delta SimpleNamespace without a content key when the chunk is reasoning-only (Gemini's thinking block before any visible text). The downstream consumer accesses delta.content and raises AttributeError: 'types.SimpleNamespace' object has no attribute 'content'. Reproduced on Gemini 2.5 Flash via --provider google-gemini-cli; any model whose stream emits a thinking delta before a content delta is at risk.
Minimal reproduction (no API quota required)
from agent.gemini_cloudcode_adapter import _make_stream_chunk
d = _make_stream_chunk(model="m", reasoning="think").choices[0].delta
assert d.content is None # AttributeError pre-patch
Observed live (hermes chat -v excerpt)
run_agent - INFO - Gemini Cloud Code Assist client created (chat_completion_stream_request, shared=False)
[thinking] **Acknowledging your input**
I've processed your acknowledgment and am ready for your next directive...
run_agent - INFO - Streaming failed before delivery: 'types.SimpleNamespace' object has no attribute 'content'
run_agent - DEBUG - Error classified: reason=unknown status=None retryable=True compress=False rotate=False fallback=False
run_agent - WARNING - API call failed (attempt 2/3) error_type=AttributeError ... summary='types.SimpleNamespace' object has no attribute 'content'
The reasoning chunk is delivered, then the next consumer access blows up. Caught at run_agent.py:8419. The AttributeError is classified as unknown and retried — which can obscure the root cause and may later be masked behind quota-related 429s on the same model.
Root cause
agent/gemini_cloudcode_adapter.py:_make_stream_chunk() (~line 453, v0.13.0):
delta_kwargs: Dict[str, Any] = {"role": "assistant"}
if content:
delta_kwargs["content"] = content
...
if reasoning:
delta_kwargs["reasoning"] = reasoning
delta_kwargs["reasoning_content"] = reasoning
delta = SimpleNamespace(**delta_kwargs)
A bare SimpleNamespace only exposes attributes that were passed; when content is empty, the field is missing entirely. Within this same adapter, sibling constructors always define content — _make_completion() uses None/joined-text (~line 384) and _empty_response() uses "" (~line 408). _make_stream_chunk() is the inconsistent one.
Proposed fix (one line)
- delta_kwargs: Dict[str, Any] = {"role": "assistant"}
- if content:
- delta_kwargs["content"] = content
+ delta_kwargs: Dict[str, Any] = {"role": "assistant", "content": content or None}
None (over "") for a stream delta tracks the delta.content: Optional[str] shape of OpenAI's ChatCompletionChunk, which is what the docstring on _GeminiStreamChunk claims to mimic.
Regression test
from agent.gemini_cloudcode_adapter import _make_stream_chunk
for label, kwargs in [
("reasoning-only", dict(model="m", reasoning="think", content="")),
("content-only", dict(model="m", content="hello")),
("tool-call-only", dict(model="m", tool_call_delta={"index":0,"name":"foo","arguments":"{}"})),
]:
d = _make_stream_chunk(**kwargs).choices[0].delta
assert hasattr(d, "content"), f"{label}: missing .content"
Pre-patch, reasoning-only and tool-call-only raise AttributeError.
Environment
hermes --version → Hermes Agent v0.13.0 (2026.5.7) ("Tenacity")
- Image:
nousresearch/hermes-agent:latest (digest at time of writing)
- Deploy: Docker Compose on macOS host,
platform: linux/amd64
- Model tested:
gemini-2.5-flash via --provider google-gemini-cli
Summary
_make_stream_chunk()inagent/gemini_cloudcode_adapter.pybuilds the streameddeltaSimpleNamespacewithout acontentkey when the chunk is reasoning-only (Gemini's thinking block before any visible text). The downstream consumer accessesdelta.contentand raisesAttributeError: 'types.SimpleNamespace' object has no attribute 'content'. Reproduced on Gemini 2.5 Flash via--provider google-gemini-cli; any model whose stream emits a thinking delta before a content delta is at risk.Minimal reproduction (no API quota required)
Observed live (
hermes chat -vexcerpt)The reasoning chunk is delivered, then the next consumer access blows up. Caught at
run_agent.py:8419. TheAttributeErroris classified asunknownand retried — which can obscure the root cause and may later be masked behind quota-related 429s on the same model.Root cause
agent/gemini_cloudcode_adapter.py:_make_stream_chunk()(~line 453, v0.13.0):A bare
SimpleNamespaceonly exposes attributes that were passed; whencontentis empty, the field is missing entirely. Within this same adapter, sibling constructors always definecontent—_make_completion()usesNone/joined-text (~line 384) and_empty_response()uses""(~line 408)._make_stream_chunk()is the inconsistent one.Proposed fix (one line)
None(over"") for a stream delta tracks thedelta.content: Optional[str]shape of OpenAI'sChatCompletionChunk, which is what the docstring on_GeminiStreamChunkclaims to mimic.Regression test
Pre-patch,
reasoning-onlyandtool-call-onlyraiseAttributeError.Environment
hermes --version→Hermes Agent v0.13.0 (2026.5.7)("Tenacity")nousresearch/hermes-agent:latest(digest at time of writing)platform: linux/amd64gemini-2.5-flashvia--provider google-gemini-cli