Release v0.51.223 — Release GQ (stage-p5 — openai-api first-class picker provider #3443 + MiniMax-M3 #3374) (#3453)

* feat: upgrade MiniMax default model to M3

Add MiniMax-M3 as the new default and prune deprecated older
versions (M2.5/M2.5-highspeed/M2.1/M2) from the model catalog.
M2.7 (and M2.7-highspeed) is retained as the legacy compatible
option for users who pin to it.

Updated:
  - api/config.py: _FALLBACK_MODELS adds minimax/MiniMax-M3 (placed
    before M2.7 so the dropdown surfaces it first)
  - api/config.py: _PROVIDER_MODELS['minimax'] adds M3 first, removes
    M2.5/M2.5-highspeed/M2.1
  - api/config.py: _PROVIDER_MODELS['minimax-cn'] adds M3 first,
    removes M2.5/M2.1/M2
  - tests/test_minimax_provider.py: updated CN catalog assertions
    to match the new {M3, M2.7} list

API URL and TTS configuration are unchanged.

Co-Authored-By: Octopus <liyuan851277048@icloud.com>

* fix(models): register openai-api as first-class picker provider

* fix(models): detect OPENAI_API_KEY as openai-api, not bare openai (#3443 Codex follow-up)

Codex review found #3444 added the openai-api picker entry but the env-detection
side still did detected_providers.add('openai') for OPENAI_API_KEY. The agent
registry has only openai-api and openai-codex (no bare openai), so a env-only
OPENAI_API_KEY setup emitted @openai: picker entries the agent can't resolve on
the send path. Detect openai-api to match the registry. Adds a regression test.

* docs(changelog): v0.51.223 — re-stamp keep-set (#3443 openai-api + #3374 MiniMax-M3); dropped #3289 + #3264 to hold per Codex

---------

Co-authored-by: octo-patch <octo-patch@github.com>
Co-authored-by: Octopus <liyuan851277048@icloud.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <[email protected]>
This commit is contained in:
nesquena-hermes 2026-06-02 18:08:01 -07:00 committed by GitHub
parent cccb97d970
commit b850a88e8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 95 additions and 13 deletions

View file

@ -3,6 +3,14 @@
## [Unreleased]
## [v0.51.223] — 2026-06-02 — Release GQ (stage-p5 — openai-api picker provider + MiniMax-M3)
### Fixed
- GPT models now appear in the model picker when hermes-agent exposes its built-in OpenAI provider under the `openai-api` slug (the one activated by `OPENAI_API_KEY` / `OPENAI_BASE_URL`, distinct from `openai-codex`). `openai-api` is now a first-class picker provider in `_PROVIDER_DISPLAY` / `_PROVIDER_MODELS` rather than an alias of `openai` — an alias would have fixed the display but broken the send path, since the agent registry has `openai-api` and not `openai`. Env detection for `OPENAI_API_KEY` was also corrected to surface `openai-api` instead of a bare `openai` the agent registry can't resolve (#3443, @rodboev).
### Changed
- MiniMax default model catalog upgraded to M3 in the model picker (#3374, @octo-patch).
## [v0.51.222] — 2026-06-02 — Release GP (stage-p4 — backend bugfix batch: title language drift + orphaned CLI sidecar prune + pin-quota lineage)
### Fixed

View file

@ -679,6 +679,7 @@ _FALLBACK_MODELS = [
# Mistral
{"provider": "Mistral", "id": "mistralai/mistral-large-latest", "label": "Mistral Large"},
# MiniMax
{"provider": "MiniMax", "id": "minimax/MiniMax-M3", "label": "MiniMax M3"},
{"provider": "MiniMax", "id": "minimax/MiniMax-M2.7", "label": "MiniMax M2.7"},
{"provider": "MiniMax", "id": "minimax/MiniMax-M2.7-highspeed", "label": "MiniMax M2.7 Highspeed"},
# Z.AI / GLM
@ -704,6 +705,7 @@ _PROVIDER_DISPLAY = {
"openrouter": "OpenRouter",
"anthropic": "Anthropic",
"openai": "OpenAI",
"openai-api": "OpenAI API",
"openai-codex": "OpenAI Codex",
"xai-oauth": "xAI Grok OAuth",
"copilot": "GitHub Copilot",
@ -1061,6 +1063,12 @@ _PROVIDER_MODELS = {
{"id": "gpt-5.4-mini", "label": "GPT-5.4 Mini"},
{"id": "gpt-5.4", "label": "GPT-5.4"},
],
"openai-api": [
{"id": "gpt-5.5", "label": "GPT-5.5"},
{"id": "gpt-5.5-mini", "label": "GPT-5.5 Mini"},
{"id": "gpt-5.4-mini", "label": "GPT-5.4 Mini"},
{"id": "gpt-5.4", "label": "GPT-5.4"},
],
"openai-codex": [
{"id": "gpt-5.5", "label": "GPT-5.5"},
{"id": "gpt-5.5-mini", "label": "GPT-5.5 Mini"},
@ -1107,17 +1115,13 @@ _PROVIDER_MODELS = {
{"id": "kimi-k2.5", "label": "Kimi K2.5"},
],
"minimax": [
{"id": "MiniMax-M3", "label": "MiniMax M3"},
{"id": "MiniMax-M2.7", "label": "MiniMax M2.7"},
{"id": "MiniMax-M2.7-highspeed", "label": "MiniMax M2.7 Highspeed"},
{"id": "MiniMax-M2.5", "label": "MiniMax M2.5"},
{"id": "MiniMax-M2.5-highspeed", "label": "MiniMax M2.5 Highspeed"},
{"id": "MiniMax-M2.1", "label": "MiniMax M2.1"},
],
"minimax-cn": [
{"id": "MiniMax-M3", "label": "MiniMax M3"},
{"id": "MiniMax-M2.7", "label": "MiniMax M2.7"},
{"id": "MiniMax-M2.5", "label": "MiniMax M2.5"},
{"id": "MiniMax-M2.1", "label": "MiniMax M2.1"},
{"id": "MiniMax-M2", "label": "MiniMax M2"},
],
# GitHub Copilot — model IDs served via the Copilot API
"copilot": [
@ -3558,7 +3562,12 @@ def get_available_models() -> dict:
if all_env.get("ANTHROPIC_API_KEY"):
detected_providers.add("anthropic")
if all_env.get("OPENAI_API_KEY"):
detected_providers.add("openai")
# hermes-agent registers its OPENAI_API_KEY/OPENAI_BASE_URL provider
# under the slug `openai-api` (there is no bare `openai` in the agent
# registry — only `openai-api` and `openai-codex`). Detecting `openai`
# here would emit `@openai:` picker entries the agent can't resolve on
# the send path, so detect `openai-api` to match the registry (#3443).
detected_providers.add("openai-api")
# openai-codex uses ChatGPT OAuth (not OPENAI_API_KEY) for its default endpoint.
# Detecting it here lets users who have both credentials configured find it in the
# picker without a manual config.yaml edit. Users without Codex OAuth will see

View file

@ -138,10 +138,8 @@ def test_minimax_cn_provider_models_match_hermes_agent_catalog():
models = config._PROVIDER_MODELS.get('minimax-cn', [])
ids = [m['id'] for m in models]
assert ids == [
'MiniMax-M3',
'MiniMax-M2.7',
'MiniMax-M2.5',
'MiniMax-M2.1',
'MiniMax-M2',
]
assert config._PROVIDER_DISPLAY.get('minimax-cn') == 'MiniMax (China)'
@ -199,10 +197,8 @@ def test_minimax_cn_detected_from_os_environ(monkeypatch, tmp_path):
assert 'minimax-cn' in groups, f"minimax-cn group missing: {groups.keys()}"
assert groups['minimax-cn']['provider'] == 'MiniMax (China)'
assert {m['id'] for m in groups['minimax-cn']['models']} == {
'MiniMax-M3',
'MiniMax-M2.7',
'MiniMax-M2.5',
'MiniMax-M2.1',
'MiniMax-M2',
}
assert 'minimax' not in groups, (
"MINIMAX_CN_API_KEY must not be collapsed into the global minimax provider"

View file

@ -0,0 +1,69 @@
"""openai-api as a first-class picker provider.
hermes-agent registers its built-in OpenAI provider as ``openai-api`` in
``hermes_cli.auth.PROVIDER_REGISTRY``. The WebUI must recognise this
slug in ``_PROVIDER_DISPLAY`` and ``_PROVIDER_MODELS`` so that GPT
models appear in the picker AND the session ``model_provider`` stays
``openai-api`` on the send path (where the agent resolves it against
its own registry).
An alias-only approach (``openai-api`` → ``openai``) fixes display but
breaks the send path because ``openai`` is not a registered provider in
the agent. See PR review discussion on #3444.
"""
import api.config as cfg
class TestOpenaiApiPickerProvider:
"""openai-api must be a first-class picker provider, not an alias."""
def test_provider_display_registered(self):
assert "openai-api" in cfg._PROVIDER_DISPLAY
def test_provider_models_registered(self):
assert "openai-api" in cfg._PROVIDER_MODELS
assert len(cfg._PROVIDER_MODELS["openai-api"]) > 0
def test_not_aliased(self):
assert "openai-api" not in cfg._PROVIDER_ALIASES
def test_canonicalise_preserves_slug(self):
assert cfg._canonicalise_provider_id("openai-api") == "openai-api"
def test_openai_canonical_unchanged(self):
assert cfg._canonicalise_provider_id("openai") == "openai"
def test_openai_codex_unchanged(self):
assert cfg._canonicalise_provider_id("openai-codex") == "openai-codex"
class TestOpenaiApiSendPath:
"""The send path must preserve openai-api, not collapse it to openai."""
def test_resolve_model_provider_preserves_openai_api(self, monkeypatch):
monkeypatch.setattr(cfg, "cfg", {
"model": {"provider": "openai-api", "default": "gpt-5.5"},
})
_model, provider, _base_url = cfg.resolve_model_provider("gpt-5.5")
assert provider == "openai-api"
def test_model_with_provider_context_preserves_openai_api(self, monkeypatch):
monkeypatch.setattr(cfg, "cfg", {
"model": {"provider": "openai-api", "default": "gpt-5.5"},
})
result = cfg.model_with_provider_context("gpt-5.5", "openai-api")
assert "openai" not in result or "openai-api" in result
class TestOpenaiApiEnvDetection:
"""OPENAI_API_KEY env detection must surface `openai-api`, not a bare `openai`
the agent registry can't resolve (#3443 detection-side, Codex follow-up)."""
def test_openai_api_key_detects_openai_api_not_bare_openai(self):
import inspect
src = inspect.getsource(cfg)
# The env-detection branch for OPENAI_API_KEY must add "openai-api".
assert 'detected_providers.add("openai-api")' in src
# And must NOT add a bare "openai" (no such slug in the agent registry).
assert 'detected_providers.add("openai")\n' not in src