hermes - ✅(Solved) Fix Gemini Code Assist streaming: AttributeError on reasoning-only deltas (_make_stream_chunk omits .content) [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#24974Fetched 2026-05-14 03:50:08
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
labeled ×4commented ×1cross-referenced ×1

_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.

Error Message

run_agent - DEBUG - Error classified: reason=unknown status=None retryable=True compress=False rotate=False fallback=False

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.

Fix Action

Fix / Workaround

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

Pre-patch, reasoning-only and tool-call-only raise AttributeError.

PR fix notes

PR #24984: fix(gemini): add missing content attribute to reasoning-only stream deltas

Description (problem / solution / changelog)

Summary

_make_stream_chunk() in agent/gemini_cloudcode_adapter.py builds the streamed delta SimpleNamespace without a content key when the chunk carries only reasoning text (Gemini thinking block before any visible content). Downstream consumers accessing delta.content raise AttributeError.

Root Cause

The delta_kwargs dict is initialized with only {"role": "assistant"}. The content key is only added when content is truthy (if content:), so reasoning-only deltas never get it. The sibling gemini_native_adapter.py already initializes delta_kwargs with "content": None — this adapter was simply missed.

Fix

Initialize delta_kwargs with "content": None so delta.content always exists, returning None for reasoning-only chunks — consistent with gemini_native_adapter.py and the OpenAI streaming contract.

Testing

  • Manual verification: _make_stream_chunk(model="m", reasoning="think").choices[0].delta.content now returns None instead of raising AttributeError
  • All 223 Gemini-related tests pass (pytest -k gemini: 223 passed, 4 skipped)

Closes #24974

Changed files

  • agent/gemini_cloudcode_adapter.py (modified, +1/-1)

Code Example

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

---

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'

---

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)

---

- delta_kwargs: Dict[str, Any] = {"role": "assistant"}
- if content:
-     delta_kwargs["content"] = content
+ delta_kwargs: Dict[str, Any] = {"role": "assistant", "content": content or None}

---

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"
RAW_BUFFERClick to expand / collapse

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 --versionHermes 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

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix Gemini Code Assist streaming: AttributeError on reasoning-only deltas (_make_stream_chunk omits .content) [1 pull requests, 1 comments, 2 participants]