Runtime identity resolved through hermes_cli.__version__ (a static 0.0.0 on source installs, rewritten by release stamping) leaked v0.0.0 into About, /api/health, User-Agents, and plugin compat, and source updates showed "couldn't reach update server" because identity and channel authority disagreed with the checkout. Now: get_version_info() resolves install stamp -> live git -> unknown, never pyproject metadata, never a package constant. Source checkouts derive identity from their reachable release tag; the completion tail of every successful install/update/historical takeover atomically rewrites install-stamp.json with that identity; a stale source stamp whose commit no longer matches HEAD defers to live git. ACP/TUI use derived_version for display and base_version for protocol fields; all ~44 runtime __version__ consumers migrated; hermes_cli.__version__ and generated _version.py are gone; release stamping only touches the native manifests external builders consume (nix/tauri/cargo) and passes release identity straight into write_install_stamp.py; pyproject.toml stays inert 0.0.0. Desktop no longer synthesizes a competing install-stamp.json: the checkout owns its stamp, and desktop-bootstrap classification keys on the bootstrap-complete marker. verify-bootstrap-version-stamp.py now cross-checks the checkout's stamp (baseVersion + commit == HEAD). Validation: 31-file focused suite green (version identity, stamping, adoption, providers, gateway, acp/tui runtime identity, api server via extras env, release graph); desktop tsc + 25 vitest green; real-repo probe: base=unknown derived=git.0635606.dirty source=git on this checkout; clean-env imports resolve entirely from this tree; windows footgun + compat-pointer scans clean.
98 lines
3.9 KiB
Python
98 lines
3.9 KiB
Python
"""Unit tests for the Fireworks AI provider profile.
|
|
|
|
Pins the profile's contract without going live: identity, alias registration,
|
|
and the pay-as-you-go model defaults (direct catalog ``/models/``
|
|
IDs, not the router-only tier).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def fireworks_profile():
|
|
"""Resolve the registered Fireworks profile through the real discovery path."""
|
|
# Importing model_tools triggers plugin discovery, registering the profile.
|
|
import model_tools # noqa: F401
|
|
import providers
|
|
|
|
profile = providers.get_provider_profile("fireworks")
|
|
assert profile is not None, "fireworks provider profile must be registered"
|
|
return profile
|
|
|
|
|
|
|
|
|
|
class TestFireworksHeaders:
|
|
def test_attribution_matches_canonical_hermes_values(self, fireworks_profile):
|
|
"""Fireworks requests carry the same attribution identity Hermes sends
|
|
everywhere else.
|
|
|
|
Asserted against the shared constant rather than the literals so a
|
|
rebrand can't leave one provider on a stale referer/title.
|
|
"""
|
|
from agent.auxiliary_client import _OR_HEADERS_BASE
|
|
|
|
headers = fireworks_profile.default_headers
|
|
assert headers["HTTP-Referer"] == _OR_HEADERS_BASE["HTTP-Referer"]
|
|
assert headers["X-Title"] == _OR_HEADERS_BASE["X-Title"]
|
|
|
|
def test_user_agent_identifies_hermes(self, fireworks_profile):
|
|
from hermes_cli.version_info import get_version_info
|
|
assert fireworks_profile.default_headers["User-Agent"] == (
|
|
f"HermesAgent/{get_version_info().base_version}"
|
|
)
|
|
|
|
|
|
|
|
|
|
class TestFireworksModelDefaults:
|
|
"""Defaults must be usable with a standard pay-as-you-go key.
|
|
|
|
PAYG keys address ``accounts/fireworks/models/...`` directly; the bundled
|
|
defaults target that (the BYOK motion) so a fresh key works out of the box,
|
|
and use the standard tier rather than turbo as the out-of-box default.
|
|
"""
|
|
|
|
def test_aux_model_is_payg_model_not_router(self, fireworks_profile):
|
|
aux = fireworks_profile.default_aux_model
|
|
assert aux.startswith("accounts/fireworks/models/"), aux
|
|
assert "/routers/" not in aux
|
|
assert "turbo" not in aux.lower()
|
|
|
|
def test_fallback_models_are_payg_models_not_routers(self, fireworks_profile):
|
|
assert fireworks_profile.fallback_models, "expected curated fallbacks"
|
|
for model in fireworks_profile.fallback_models:
|
|
assert model.startswith("accounts/fireworks/models/"), model
|
|
assert "/routers/" not in model
|
|
assert "turbo" not in model.lower(), model
|
|
|
|
|
|
class TestFireworksReasoning:
|
|
@pytest.mark.parametrize(
|
|
"provider, reasoning_config, expect_top_level, expect_generic",
|
|
[
|
|
# Fireworks: thinking-off goes out as the documented top-level control, never as the
|
|
# nested ``extra_body.reasoning`` the API 400s on (#109774, salvaged from #109807).
|
|
("fireworks", {"enabled": False}, {"reasoning_effort": "none"}, False),
|
|
("fireworks", {"enabled": True, "effort": "low"}, {"reasoning_effort": "low"}, False),
|
|
# Control: a route without a reasoning-aware profile keeps the generic fallback.
|
|
("unregistered-gateway", {"enabled": False}, {}, True),
|
|
],
|
|
)
|
|
def test_auxiliary_reasoning_wire_shape(
|
|
self, fireworks_profile, provider, reasoning_config, expect_top_level, expect_generic
|
|
):
|
|
from agent.auxiliary_client import _build_call_kwargs
|
|
|
|
kwargs = _build_call_kwargs(
|
|
provider, "accounts/fireworks/models/glm-5p2",
|
|
[{"role": "user", "content": "Generate a title"}],
|
|
reasoning_config=reasoning_config, base_url="https://api.fireworks.ai/inference/v1",
|
|
task="title_generation",
|
|
)
|
|
|
|
assert {k: v for k, v in kwargs.items() if k == "reasoning_effort"} == expect_top_level
|
|
assert ("reasoning" in kwargs.get("extra_body", {})) is expect_generic
|