GPT-5 custom endpoints ignoring explicit api_mode setting

NousResearch/hermes-agent · cluster 5718 · agent

Data as of

Nothing open on GitHub. Fix merged in #10548. Check it covers your case.

The short answer

0open issues
0open PRs
5items grouped
Known?
Yes. 5 related items grouped here, 3 issues already closed.
Fix event
A fix was merged to main in PR #10548. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PRs #10548, #115738. That is separate from the fix event.
Still open
Nothing open on GitHub.

Reports and fixes around custom GPT-5 provider endpoints not respecting an explicitly configured api_mode, defaulting to codex_responses instead of chat_completions. Includes proposed fixes and a salvage attempt for a related prior change.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #10548 fix: respect explicit api_mode for custom GPT-5 endpoints

    merged to main

    files: gateway/run.py, run_agent.py, tests/gateway/test_background_process_notifications.py

    Show the diff
    • gateway/run.py
      @@ -486,9 +486,14 @@ def _parse_session_key(session_key: str) -> "dict | None":
           """Parse a session key into its component parts.
       
           Session keys follow the format
      -    ``agent:main:{platform}:{chat_type}:{chat_id}[:{thread_id}[:{user_id}]]``.
      +    ``agent:main:{platform}:{chat_type}:{chat_id}[:{extra}...]``.
           Returns a dict with ``platform``, ``chat_type``, ``chat_id``, and
           optionally ``thread_id`` keys, or None if the key doesn't match.
      +
      +    The 6th element is only returned as ``thread_id`` for chat types where
      +    it is unambiguous (``dm`` and ``thread``).  For group/channel sessions
      +    the suffix may be a user_id (per-user isolation) rather than a
      +    thread_id, so we leave ``thread_id`` out to avoid mis-routing.
           """
           parts = session_key.split(":")
           if len(parts) >= 5 and parts[0] == "agent" and parts[1] == "main":
      @@ -497,7 +502,7 @@ def _parse_session_key(session_key: str) -> "dict | None":
                   "chat_type": parts[3],
                   "chat_id": parts[4],
               }
      -        if len(parts) > 5:
      +        if len(parts) > 5 and parts[3] in ("dm", "thread"):
                   result["thread_id"] = parts[5]
               return result
           return None
    • run_agent.py
      @@ -721,8 +721,11 @@ def __init__(
               # Responses there. ACP runtimes are excluded: CopilotACPClient
               # handles its own routing and does not implement the Responses API
               # surface.
      +        # When api_mode was explicitly provided, respect it — the user
      +        # knows what their endpoint supports (#10473).
               if (
      -            self.api_mode == "chat_completions"
      +            api_mode is None
      +            and self.api_mode == "chat_completions"
                   and self.provider != "copilot-acp"
                   and not str(self.base_url or "").lower().startswith("acp://copilot")
                   and not str(self.base_url or "").lower().startswith("acp+tcp://")
    • tests/gateway/test_background_process_notifications.py
      @@ -383,15 +383,27 @@ def test_parse_session_key_valid():
       
       
       def test_parse_session_key_with_extra_parts():
      -    """Thread ID (6th part) is extracted; further parts are ignored."""
      +    """6th part in a group key may be a user_id, not a thread_id — omit it."""
           result = _parse_session_key("agent:main:discord:group:chan123:thread456")
      -    assert result == {"platform": "discord", "chat_type": "group", "chat_id": "chan123", "thread_id": "thread456"}
      +    assert result == {"platform": "discord", "chat_type": "group", "chat_id": "chan123"}
       
       
       def test_parse_session_key_with_user_id_part():
      -    """7th part (user_id) is ignored — only up to thread_id is extracted."""
      -    result = _parse_session_key("agent:main:telegram:group:chat1:thread42:user99")
      -    assert result == {"platform": "telegram", "chat_type": "group", "chat_id": "chat1", "thread_id": "thread42"}
      +    """Group keys with per-user isolation have user_id as 6th part — don't return as thread_id."""
      +    result = _parse_session_key("agent:main:telegram:group:chat1:user99")
      +    assert result == {"platform": "telegram", "chat_type": "group", "chat_id": "chat1"}
      +
      +
      +def test_parse_session_key_dm_with_thread():
      +    """DM keys use parts[5] as thread_id unambiguously."""
      +    result = _parse_session_key("agent:main:telegram:dm:chat1:topic42")
      +    assert result == {"platform": "telegram", "chat_type": "dm", "chat_id": "chat1", "thread_id": "topic42"}
      +
      +
      +def test_parse_session_key_thread_chat_type():
      +    """Thread-typed keys use parts[5] as thread_id unambiguously."""
      +    result = _parse_session_key("agent:main:discord:thread:chan1:thread99")
      +    assert result == {"platform": "discord", "chat_type": "thread", "chat_id": "chan1", "thread_id": "thread99"}
       
       
       def test_parse_session_key_too_short():
  • #115738 fix(fallback): entries naming a providers block keep its declared transport on activation (#33062, #81932)

    merged to main

    files: agent/chat_completion_helpers.py, tests/agent/test_fallback_api_mode_preservation.py, website/docs/integrations/providers.md

Closed issues · 3
  • Closed#5718openai-codex provider should allow configurable api_mode (chat_completions vs codex_responses)
  • Closed#10473[Bug]: custom GPT-5 endpoint is forced to codex_responses, ignoring explicit chat_completions
  • Closed#33062[Bug]: Custom GPT-5 providers can ignore explicit api_mode
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.31: the share of item 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.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
agent
Merged PRs
#10548, #115738
Closed issues
3
Attempts
0 PRs closed without merging
Fix link checked
19 Sep 2026

For your agent

The same facts, typed.