# NousResearch/hermes-agent · Gemini streaming incompatibility with stream_options argument

## Open now

- Nothing open on GitHub.

A bug was identified where stream_options is passed to Gemini's completions API, which does not support that keyword argument. Multiple fix attempts address omitting stream_options specifically for Gemini streaming calls.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #54653. Verify it addresses your case.
- **Recommended:** read PR #54653 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-54653`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.50`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #14387: \[Bug\]: 📝 Error: Completions.create() got an unexpected keyword argument 'stream_options'

## Prior & competing PRs

- **#54653: fix(agent): omit stream_options for native Gemini streaming**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `agent/chat_completion_helpers.py`, `tests/run_agent/test_streaming.py`
  - fix hunks:
  - `agent/chat_completion_helpers.py`

    ```diff
    @@ -28,6 +28,7 @@
     from hermes_cli.timeouts import get_provider_request_timeout, get_provider_stale_timeout
     from hermes_constants import PARTIAL_STREAM_STUB_ID, FINISH_REASON_LENGTH
     from agent.error_classifier import FailoverReason
    +from agent.gemini_native_adapter import is_native_gemini_base_url
     from agent.model_metadata import is_local_endpoint
     from agent.message_sanitization import (
         _sanitize_surrogates,
    ```

    ```diff
    @@ -1911,7 +1912,6 @@ def _call_chat_completions():
             stream_kwargs = {
                 **api_kwargs,
                 "stream": True,
    -            "stream_options": {"include_usage": True},
                 "timeout": _httpx.Timeout(
                     connect=_conn_cap,
                     read=_stream_read_timeout,
    ```

    ```diff
    @@ -1919,6 +1919,14 @@ def _call_chat_completions():
                     pool=_conn_cap,
                 ),
             }
    +        # OpenAI's `stream_options={"include_usage": True}` drives usage
    +        # accounting on OpenAI-compatible endpoints (incl. the Gemini OpenAI
    +        # compat shim and aggregators like OpenRouter).  Google's *native*
    +        # Gemini REST endpoint rejects the keyword outright
    +        # (`Completions.create() got an unexpected keyword argument
    +        # 'stream_options'`), so omit it only for that endpoint.
    +        if not is_native_gemini_base_url(agent.base_url):
    +            stream_kwargs["stream_options"] = {"include_usage": True}
             request_client = _set_request_client(
                 agent._create_request_openai_client(
                     reason="chat_completion_stream_request",
    ```
  - `tests/run_agent/test_streaming.py`

    ```diff
    @@ -95,6 +95,99 @@ def test_text_only_response(self, mock_close, mock_create):
             assert response.usage is not None
             assert response.usage.completion_tokens == 3
     
    +    @patch("run_agent.AIAgent._create_request_openai_client")
    +    @patch("run_agent.AIAgent._close_request_openai_client")
    +    def test_native_gemini_endpoint_omits_stream_options(self, mock_close, mock_create):
    +        """Google's native Gemini REST endpoint rejects OpenAI-only stream_options."""
    +        from run_agent import AIAgent
    +
    +        mock_client = MagicMock()
    +        mock_client.chat.completions.create.return_value = iter([
    +            _make_stream_chunk(content="Paris", finish_reason="stop", model="gemini"),
    +        ])
    +        mock_create.return_value = mock_client
    +
    +        agent = AIAgent(
    +            api_key="[redacted:secret-assignment]",
    +            base_url="https://generativelanguage.googleapis.com/v1beta",
    +            model="gemini-3-flash-preview",
    +            provider="gemini",
    +            quiet_mode=True,
    +            skip_context_files=True,
    +            skip_memory=True,
    +        )
    +        agent.api_mode = "chat_completions"
    +        agent._interrupt_requested = False
    +
    +        response = agent._interruptible_streaming_api_call({})
    +
    +        assert response.choices[0].message.content == "Paris"
    +        call_kwargs = mock_client.chat.completions.create.call_args.kwargs
    +        assert call_kwargs["stream"] is True
    +        assert "stream_options" not in call_kwargs
    +
    +    @patch("run_agent.AIAgent._create_request_openai_client")
    +    @patch("run_agent.AIAgent._close_request_openai_client")
    +    def test_gemini_openai_compat_shim_keeps_stream_options(self, mock_close, mock_create):
    +        """The Gemini OpenAI-compat shim (.../openai) accepts stream_options."""
    +        from run_agent import AIAgent
    +
    +        mock_client = MagicMock()
    +        mock_client.chat.completions.create.return_value = iter([
    +            _make_stream_chunk(content="ok", finish_reason="stop", model="gemini"),
    +            _make_empty_chunk(usage=SimpleNamespace(prompt_tokens=2, completion_tokens=1)),
    +        ])
    +        mock_create.return_value = mock_client
    +
    +        agent = AIAgent(
    +            api_key="[redacted:secret-assignment]",
    +            base_url="https://generativelanguage.googleapis.com/v1beta/openai",
    +            model="gemini-3-flash-preview",
    +            provider="gemini",
    +            quiet_mode=True,
    +            skip_context_files=True,
    +            skip_memory=True,
    +        )
    +        agent.api_mode = "chat_completions"
    +        agent._interrupt_requested = False
    +
    +        response = agent._interruptible_streaming_api_call({})
    +
    +        assert response.choices[0].message.content == "ok"
    +        call_kwargs = mock_client.chat.completions.create.call_args.kwargs
    +        assert call_kwargs["stream_options"] == {"include_usage": True}
    +
    +    @patch("run_agent.AIAgent._create_request_openai_client")
    +    @patch("run_agent.AIAgent._close_request_openai_client")
    +    def test_openai_compatible_streaming_keeps_stream_options(self, mock_close, mock_create):
    +        """OpenAI-compatible aggregators still request final usage chunks."""
    +        from run_agent import AIAgent
    +
    +        mock_client = MagicMock()
    +        mock_client.chat.completions.create.return_value = iter([
    +            _make_stream_chunk(content="ok", finish_reason="stop", model="test-model"),
    +            _make_empty_chunk(usage=SimpleNamespace(prompt_tokens=2, completion_tokens=1)),
    +        ])
    +        mock_create.return_value = mock_client
    +
    +        agent = AIAgent(
    +            api_key="[redacted:secret-assignment]",
    +            base_url="https://openrouter.ai/api/v1",
    +            model="test/model",
    +            provider="openrouter",
    +            quiet_mode=True,
    +            skip_context_files=True,
    +            skip_memory=True,
    +        )
    +        agent.api_mode = "chat_completions"
    +        agent._interrupt_requested = False
    +
    +        response = agent._interruptible_streaming_api_call({})
    +
    +        assert response.choices[0].message.content == "ok"
    +        call_kwargs = mock_client.chat.completions.create.call_args.kwargs
    +        assert call_kwargs["stream_options"] == {"include_usage": True}
    +
         @patch("run_agent.AIAgent._create_request_openai_client")
         @patch("run_agent.AIAgent._close_request_openai_client")
         def test_tool_call_response(self, mock_close, mock_create):
    ```
- **#14392: fix(agent): omit stream_options for Gemini streaming**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `032d70214`, `#54653`, `#14387`
- **#14460: fix(agent): skip stream_options for Gemini — unsupported keyword**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#14392`, `#14387`

## Fix status

- Merged to main in PR #54653 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-29`.
- Subsystem: `agent`
