hermes-agent/tests/hermes_cli/test_fireworks_provider.py

184 lines
6.9 KiB
Python

"""Focused tests for Fireworks AI first-class provider wiring.
These tests pin the wiring that makes Fireworks a real provider — alias
resolution through both CLI resolvers, config/doctor/overlay registration,
and credential/base-URL resolution — without
any live network calls.
"""
from __future__ import annotations
import contextlib
import io
import sys
import types
from argparse import Namespace
import pytest
if "dotenv" not in sys.modules:
fake_dotenv = types.ModuleType("dotenv")
fake_dotenv.load_dotenv = lambda *args, **kwargs: None
sys.modules["dotenv"] = fake_dotenv
from hermes_cli.auth import resolve_api_key_provider_credentials
from hermes_cli.models import CANONICAL_PROVIDERS, _PROVIDER_LABELS, normalize_provider
@pytest.fixture(autouse=True)
def _clear_provider_env(monkeypatch):
for key in ("FIREWORKS_API_KEY", "FIREWORKS_BASE_URL"):
monkeypatch.delenv(key, raising=False)
class TestFireworksAliases:
"""Both CLI resolvers must map the aliases — the plugin's aliases= tuple is
NOT consulted by these static maps, so they need explicit coverage."""
@pytest.mark.parametrize("alias", ["fireworks", "fireworks-ai", "fw", "FW", " Fireworks-AI "])
def test_models_normalize_provider(self, alias):
assert normalize_provider(alias) == "fireworks"
@pytest.mark.parametrize("alias", ["fireworks", "fireworks-ai", "fw"])
def test_providers_normalize_provider(self, alias):
from hermes_cli.providers import normalize_provider as normalize_in_providers
assert normalize_in_providers(alias) == "fireworks"
class TestFireworksOrdering:
"""Fireworks participates in the canonical provider catalog."""
def test_present_in_canonical_providers(self):
slugs = [p.slug for p in CANONICAL_PROVIDERS]
assert "fireworks" in slugs
def test_has_a_label(self):
assert _PROVIDER_LABELS.get("fireworks") == "Fireworks AI"
class TestFireworksConfigRegistry:
def test_optional_env_vars_include_fireworks(self):
from hermes_cli.config import OPTIONAL_ENV_VARS
assert "FIREWORKS_API_KEY" in OPTIONAL_ENV_VARS
assert OPTIONAL_ENV_VARS["FIREWORKS_API_KEY"]["category"] == "provider"
assert OPTIONAL_ENV_VARS["FIREWORKS_API_KEY"]["password"] is True
assert "FIREWORKS_BASE_URL" not in OPTIONAL_ENV_VARS
class TestFireworksOverlay:
def test_overlay_exists(self):
from hermes_cli.providers import HERMES_OVERLAYS
assert "fireworks" in HERMES_OVERLAYS
overlay = HERMES_OVERLAYS["fireworks"]
assert overlay.transport == "openai_chat"
assert overlay.base_url_override == "https://api.fireworks.ai/inference/v1"
assert not overlay.base_url_env_var
assert not overlay.is_aggregator
class TestFireworksDoctor:
def test_provider_env_hints_include_fireworks(self):
from hermes_cli.doctor import _PROVIDER_ENV_HINTS
assert "FIREWORKS_API_KEY" in _PROVIDER_ENV_HINTS
def test_slash_form_model_is_not_flagged_as_vendor_prefixed(self, monkeypatch, tmp_path):
"""Fireworks' native model IDs are slash-form (accounts/fireworks/...),
so doctor must NOT warn that provider should be 'openrouter' / the prefix
dropped — that heuristic is for aggregator vendor slugs only."""
from hermes_cli import doctor as doctor_mod
home = tmp_path / ".hermes"
home.mkdir(parents=True)
(home / "config.yaml").write_text(
"model:\n"
" provider: fireworks\n"
" default: accounts/fireworks/models/kimi-k2p6\n"
"memory: {}\n",
encoding="utf-8",
)
(home / ".env").write_text("FIREWORKS_API_KEY=fw_test\n", encoding="utf-8")
project = tmp_path / "project"
project.mkdir()
monkeypatch.setattr(doctor_mod, "HERMES_HOME", home)
monkeypatch.setattr(doctor_mod, "PROJECT_ROOT", project)
monkeypatch.setattr(doctor_mod, "_DHH", str(home))
monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test")
# Keep the run offline and cheap.
import httpx
monkeypatch.setattr(httpx, "get", lambda *a, **k: types.SimpleNamespace(status_code=200))
monkeypatch.setitem(
sys.modules,
"model_tools",
types.SimpleNamespace(check_tool_availability=lambda *a, **k: ([], []), TOOLSET_REQUIREMENTS={}),
)
with contextlib.suppress(Exception):
from hermes_cli import auth as _auth_mod
monkeypatch.setattr(_auth_mod, "get_nous_auth_status", lambda: {})
monkeypatch.setattr(_auth_mod, "get_codex_auth_status", lambda: {})
buf = io.StringIO()
with contextlib.suppress(SystemExit), contextlib.redirect_stdout(buf):
doctor_mod.run_doctor(Namespace(fix=False))
out = buf.getvalue()
assert "vendor-prefixed" not in out
assert "vendor/model slug" not in out
class TestFireworksCredentials:
def test_resolves_default_base_url(self, monkeypatch):
monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test_key")
creds = resolve_api_key_provider_credentials("fireworks")
assert creds["api_key"] == "fw_test_key"
assert creds["base_url"] == "https://api.fireworks.ai/inference/v1"
class TestFireworksAuxiliary:
"""resolve_provider_client wires the BYOK key and PAYG-safe aux model."""
def _resolve(self, name):
from unittest.mock import patch
from agent.auxiliary_client import resolve_provider_client
with patch("agent.auxiliary_client.OpenAI") as mock_openai:
mock_openai.return_value = object()
client, model = resolve_provider_client(name)
return client, model, mock_openai.call_args.kwargs
def test_client_has_no_partner_attribution_headers(self, monkeypatch):
monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test_key")
client, model, kwargs = self._resolve("fireworks")
assert client is not None
headers = kwargs.get("default_headers", {})
assert "HTTP-Referer" not in headers
assert "X-Title" not in headers
assert kwargs["base_url"] == "https://api.fireworks.ai/inference/v1"
def test_aux_model_is_payg_safe(self, monkeypatch):
monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test_key")
_, model, _ = self._resolve("fireworks")
assert model.startswith("accounts/fireworks/models/")
assert "/routers/" not in model
assert "turbo" not in model.lower()
def test_alias_resolves_through_aux_client(self, monkeypatch):
monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test_key")
client, _, _ = self._resolve("fw")
assert client is not None
class TestFireworksModelMetadata:
def test_url_infers_fireworks(self):
from agent.model_metadata import _infer_provider_from_url
assert _infer_provider_from_url("https://api.fireworks.ai/inference/v1") == "fireworks"