fix: detect shared OpenCode API key
This commit is contained in:
1 parent
95cee09c31
commit
1f51ef14d0
3 files changed
+36
-5
No files matched your search
@@ -3,6 +3,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Detect a shared `OPENCODE_API_KEY` as enabling both OpenCode Zen and OpenCode Go provider groups, matching Hermes Agent bridge environments that expose one OpenCode credential.
|
||||
|
||||
## [v0.51.159] — 2026-05-29 — Release EE (stage-batch41 — 5-PR low-risk cleanup: Gateway tool-progress forwarding + shutdown diagnostics + CLI snippet-limit parity + numpad-Enter submit + sync-chat notes guardrail)
|
||||
|
||||
### Changed
|
||||
|
||||
+3
-2
@@ -3437,6 +3437,7 @@ def get_available_models() -> dict:
|
||||
"XIAOMI_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY",
|
||||
"OPENCODE_GO_API_KEY",
|
||||
"OPENCODE_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
"MINIMAX_CN_API_KEY",
|
||||
"XAI_API_KEY",
|
||||
@@ -3478,9 +3479,9 @@ def get_available_models() -> dict:
|
||||
detected_providers.add("x-ai")
|
||||
if all_env.get("MISTRAL_API_KEY"):
|
||||
detected_providers.add("mistralai")
|
||||
if all_env.get("OPENCODE_ZEN_API_KEY"):
|
||||
if all_env.get("OPENCODE_ZEN_API_KEY") or all_env.get("OPENCODE_API_KEY"):
|
||||
detected_providers.add("opencode-zen")
|
||||
if all_env.get("OPENCODE_GO_API_KEY"):
|
||||
if all_env.get("OPENCODE_GO_API_KEY") or all_env.get("OPENCODE_API_KEY"):
|
||||
detected_providers.add("opencode-go")
|
||||
# AWS Bedrock uses IAM credentials rather than a single API key.
|
||||
# Detect when both access key and secret are available (#2720).
|
||||
|
||||
@@ -3,7 +3,6 @@ Tests for OpenCode Zen and OpenCode Go provider support.
|
||||
Verifies provider registration in display/model catalogs and
|
||||
env-var fallback detection.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
import pytest
|
||||
@@ -96,6 +95,29 @@ def test_opencode_go_detected_via_env_key(monkeypatch):
|
||||
_models_with_env_key(monkeypatch, "OPENCODE_GO_API_KEY", "OpenCode Go")
|
||||
|
||||
|
||||
def test_shared_opencode_api_key_detects_zen_and_go(monkeypatch):
|
||||
"""A shared OpenCode bridge key should enable both OpenCode surfaces."""
|
||||
fake_mod = types.ModuleType("hermes_cli.models")
|
||||
fake_mod.list_available_providers = None
|
||||
monkeypatch.setitem(sys.modules, "hermes_cli.models", fake_mod)
|
||||
monkeypatch.delattr(fake_mod, "list_available_providers")
|
||||
|
||||
old_cfg = dict(config.cfg)
|
||||
config.cfg["model"] = {}
|
||||
config.cfg.pop("custom_providers", None)
|
||||
monkeypatch.delenv("OPENCODE_ZEN_API_KEY", raising=False)
|
||||
monkeypatch.delenv("OPENCODE_GO_API_KEY", raising=False)
|
||||
monkeypatch.setenv("OPENCODE_API_KEY", "test-key")
|
||||
try:
|
||||
result = config.get_available_models()
|
||||
providers = {g["provider"] for g in result["groups"]}
|
||||
assert "OpenCode Zen" in providers
|
||||
assert "OpenCode Go" in providers
|
||||
finally:
|
||||
config.cfg.clear()
|
||||
config.cfg.update(old_cfg)
|
||||
|
||||
|
||||
def test_openai_codex_model_catalog_includes_gpt54():
|
||||
"""openai-codex catalog must include gpt-5.4 and the standard Codex lineup."""
|
||||
assert "openai-codex" in config._PROVIDER_MODELS
|
||||
@@ -117,7 +139,9 @@ def test_live_models_handler_delegates_to_provider_model_ids():
|
||||
rather than maintain its own per-provider fetch logic.
|
||||
"""
|
||||
import pathlib
|
||||
routes_src = (pathlib.Path(__file__).parent.parent / "api" / "routes.py").read_text()
|
||||
routes_src = (
|
||||
pathlib.Path(__file__).parent.parent / "api" / "routes.py"
|
||||
).read_text(encoding="utf-8")
|
||||
assert "provider_model_ids" in routes_src, (
|
||||
"_handle_live_models must call hermes_cli.models.provider_model_ids() "
|
||||
"to delegate all provider-specific live-fetch logic to the agent"
|
||||
@@ -139,7 +163,9 @@ def test_live_models_ui_no_longer_skips_any_provider():
|
||||
handles them all (with graceful fallback to static lists).
|
||||
"""
|
||||
import pathlib
|
||||
ui_src = (pathlib.Path(__file__).parent.parent / "static" / "ui.js").read_text()
|
||||
ui_src = (
|
||||
pathlib.Path(__file__).parent.parent / "static" / "ui.js"
|
||||
).read_text(encoding="utf-8")
|
||||
# The old exclusion list must be gone
|
||||
assert "includes(provider)" not in ui_src or "anthropic" not in ui_src[:ui_src.find("includes(provider)")+100], (
|
||||
"_fetchLiveModels must not skip anthropic, google, or gemini — "
|
||||
|
||||
Reference in new issue
Block a user