fix(xai-http): preserve ~/.hermes/.env fallback and XAI_STT_BASE_URL precedence

The new resolve_xai_http_credentials() resolver was using os.getenv()
for the XAI_API_KEY/XAI_BASE_URL fallback path, which dropped the
~/.hermes/.env contract guarded by PR #17140 / #17163. Users with
XAI_API_KEY in dotenv only would see "No xAI credentials found" even
though the key was configured.

Separately, _transcribe_xai started consulting creds["base_url"] (which
always returns at least the default https://api.x.ai/v1) ahead of the
public XAI_STT_BASE_URL env override, so the per-tool override stopped
working.

- tools/xai_http.py: add module-level get_env_value() wrapper that
  reads ~/.hermes/.env first (via hermes_cli.config.get_env_value),
  then os.environ. Resolver uses it for the API-key/base-url fallback.
- tools/transcription_tools.py: restore precedence so XAI_STT_BASE_URL
  wins over creds["base_url"].
- tests/tools/test_transcription_dotenv_fallback.py +
  tests/tools/test_tts_dotenv_fallback.py: repoint the per-call-site
  patches at the new resolution point (tools.xai_http.get_env_value).
  The end-to-end regression-guard test (which patches load_env) is
  unchanged and still passes.
This commit is contained in:
Jaaneek 2026-05-15 18:27:54 +01:00 committed by Teknium
parent 9eef53b960
commit e13c1b8060
4 changed files with 42 additions and 10 deletions

View file

@ -5,6 +5,25 @@ from __future__ import annotations
import os
from typing import Dict
try:
from hermes_cli.config import get_env_value as _hermes_get_env_value
except Exception:
_hermes_get_env_value = None
def get_env_value(name: str, default=None):
"""Read ``name`` from ``~/.hermes/.env`` first, then ``os.environ``.
Wraps :func:`hermes_cli.config.get_env_value` so tests can patch
``tools.xai_http.get_env_value`` to inject dotenv-only secrets into the
xAI credential resolver.
"""
if _hermes_get_env_value is not None:
value = _hermes_get_env_value(name)
if value is not None:
return value
return os.environ.get(name, default)
def hermes_xai_user_agent() -> str:
"""Return a stable Hermes-specific User-Agent for xAI HTTP calls."""
@ -19,8 +38,11 @@ def resolve_xai_http_credentials() -> Dict[str, str]:
"""Resolve bearer credentials for direct xAI HTTP endpoints.
Prefers Hermes-managed xAI OAuth credentials when available, then falls back
to ``XAI_API_KEY`` from the environment. This keeps direct xAI endpoints
(images, TTS, STT, etc.) aligned with the main runtime auth model.
to ``XAI_API_KEY`` resolved via ``hermes_cli.config.get_env_value`` so keys
stored in ``~/.hermes/.env`` (the standard Hermes location) are honored
not just ones already exported into ``os.environ``. This keeps direct xAI
endpoints (images, TTS, STT, etc.) aligned with the main runtime auth model
and preserves the regression contract from PR #17140 / #17163.
"""
try:
from hermes_cli.runtime_provider import resolve_runtime_provider
@ -52,8 +74,8 @@ def resolve_xai_http_credentials() -> Dict[str, str]:
except Exception:
pass
api_key = os.getenv("XAI_API_KEY", "").strip()
base_url = (os.getenv("XAI_BASE_URL") or "https://api.x.ai/v1").strip().rstrip("/")
api_key = str(get_env_value("XAI_API_KEY") or "").strip()
base_url = str(get_env_value("XAI_BASE_URL") or "https://api.x.ai/v1").strip().rstrip("/")
return {
"provider": "xai",
"api_key": api_key,