fix: remove legacy compression.summary_* config and env var fallbacks (#8992)
Remove the backward-compat code paths that read compression provider/model settings from legacy config keys and env vars, which caused silent failures when auto-detection resolved to incompatible backends. What changed: - Remove compression.summary_model, summary_provider, summary_base_url from DEFAULT_CONFIG and cli.py defaults - Remove backward-compat block in _resolve_task_provider_model() that read from the legacy compression section - Remove _get_auxiliary_provider() and _get_auxiliary_env_override() helper functions (AUXILIARY_*/CONTEXT_* env var readers) - Remove env var fallback chain for per-task overrides - Update hermes config show to read from auxiliary.compression - Add config migration (v16→17) that moves non-empty legacy values to auxiliary.compression and strips the old keys - Update example config and openclaw migration script - Remove/update tests for deleted code paths Compression model/provider is now configured exclusively via: auxiliary.compression.provider / auxiliary.compression.model Closes #8923
This commit is contained in:
@@ -17,7 +17,6 @@ from agent.auxiliary_client import (
|
||||
call_llm,
|
||||
async_call_llm,
|
||||
_read_codex_access_token,
|
||||
_get_auxiliary_provider,
|
||||
_get_provider_chain,
|
||||
_is_payment_error,
|
||||
_try_payment_fallback,
|
||||
@@ -32,12 +31,6 @@ def _clean_env(monkeypatch):
|
||||
"OPENROUTER_API_KEY", "OPENAI_BASE_URL", "OPENAI_API_KEY",
|
||||
"OPENAI_MODEL", "LLM_MODEL", "NOUS_INFERENCE_BASE_URL",
|
||||
"ANTHROPIC_API_KEY", "ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN",
|
||||
# Per-task provider/model/direct-endpoint overrides
|
||||
"AUXILIARY_VISION_PROVIDER", "AUXILIARY_VISION_MODEL",
|
||||
"AUXILIARY_VISION_BASE_URL", "AUXILIARY_VISION_API_KEY",
|
||||
"AUXILIARY_WEB_EXTRACT_PROVIDER", "AUXILIARY_WEB_EXTRACT_MODEL",
|
||||
"AUXILIARY_WEB_EXTRACT_BASE_URL", "AUXILIARY_WEB_EXTRACT_API_KEY",
|
||||
"CONTEXT_COMPRESSION_PROVIDER", "CONTEXT_COMPRESSION_MODEL",
|
||||
):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
|
||||
@@ -568,29 +561,6 @@ class TestGetTextAuxiliaryClient:
|
||||
call_kwargs = mock_openai.call_args
|
||||
assert call_kwargs.kwargs["base_url"] == "http://localhost:1234/v1"
|
||||
|
||||
def test_task_direct_endpoint_override(self, monkeypatch):
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_BASE_URL", "http://localhost:2345/v1")
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_API_KEY", "task-key")
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_MODEL", "task-model")
|
||||
with patch("agent.auxiliary_client.OpenAI") as mock_openai:
|
||||
client, model = get_text_auxiliary_client("web_extract")
|
||||
assert model == "task-model"
|
||||
assert mock_openai.call_args.kwargs["base_url"] == "http://localhost:2345/v1"
|
||||
assert mock_openai.call_args.kwargs["api_key"] == "task-key"
|
||||
|
||||
def test_task_direct_endpoint_without_openai_key_uses_placeholder(self, monkeypatch):
|
||||
"""Local endpoints without an API key should use 'no-key-required' placeholder."""
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_BASE_URL", "http://localhost:2345/v1")
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_MODEL", "task-model")
|
||||
with patch("agent.auxiliary_client.OpenAI") as mock_openai:
|
||||
client, model = get_text_auxiliary_client("web_extract")
|
||||
assert client is not None
|
||||
assert model == "task-model"
|
||||
assert mock_openai.call_args.kwargs["api_key"] == "no-key-required"
|
||||
assert mock_openai.call_args.kwargs["base_url"] == "http://localhost:2345/v1"
|
||||
|
||||
def test_custom_endpoint_uses_config_saved_base_url(self, monkeypatch):
|
||||
config = {
|
||||
"model": {
|
||||
@@ -879,73 +849,9 @@ class TestAuxiliaryPoolAwareness:
|
||||
|
||||
|
||||
|
||||
class TestGetAuxiliaryProvider:
|
||||
"""Tests for _get_auxiliary_provider env var resolution."""
|
||||
|
||||
def test_no_task_returns_auto(self):
|
||||
assert _get_auxiliary_provider() == "auto"
|
||||
assert _get_auxiliary_provider("") == "auto"
|
||||
|
||||
def test_auxiliary_prefix_takes_priority(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_VISION_PROVIDER", "openrouter")
|
||||
assert _get_auxiliary_provider("vision") == "openrouter"
|
||||
|
||||
def test_context_prefix_fallback(self, monkeypatch):
|
||||
monkeypatch.setenv("CONTEXT_COMPRESSION_PROVIDER", "nous")
|
||||
assert _get_auxiliary_provider("compression") == "nous"
|
||||
|
||||
def test_auxiliary_prefix_over_context_prefix(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_COMPRESSION_PROVIDER", "openrouter")
|
||||
monkeypatch.setenv("CONTEXT_COMPRESSION_PROVIDER", "nous")
|
||||
assert _get_auxiliary_provider("compression") == "openrouter"
|
||||
|
||||
def test_auto_value_treated_as_auto(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_VISION_PROVIDER", "auto")
|
||||
assert _get_auxiliary_provider("vision") == "auto"
|
||||
|
||||
def test_whitespace_stripped(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_VISION_PROVIDER", " openrouter ")
|
||||
assert _get_auxiliary_provider("vision") == "openrouter"
|
||||
|
||||
def test_case_insensitive(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_VISION_PROVIDER", "OpenRouter")
|
||||
assert _get_auxiliary_provider("vision") == "openrouter"
|
||||
|
||||
def test_main_provider(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_PROVIDER", "main")
|
||||
assert _get_auxiliary_provider("web_extract") == "main"
|
||||
|
||||
|
||||
class TestTaskSpecificOverrides:
|
||||
"""Integration tests for per-task provider routing via get_text_auxiliary_client(task=...)."""
|
||||
|
||||
def test_text_with_vision_provider_override(self, monkeypatch):
|
||||
"""AUXILIARY_VISION_PROVIDER should not affect text tasks."""
|
||||
monkeypatch.setenv("AUXILIARY_VISION_PROVIDER", "nous")
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
|
||||
with patch("agent.auxiliary_client.OpenAI"):
|
||||
client, model = get_text_auxiliary_client() # no task → auto
|
||||
assert model == "google/gemini-3-flash-preview" # OpenRouter, not Nous
|
||||
|
||||
def test_compression_task_reads_context_prefix(self, monkeypatch):
|
||||
"""Compression task should check CONTEXT_COMPRESSION_PROVIDER env var."""
|
||||
monkeypatch.setenv("CONTEXT_COMPRESSION_PROVIDER", "nous")
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key") # would win in auto
|
||||
with patch("agent.auxiliary_client._read_nous_auth") as mock_nous, \
|
||||
patch("agent.auxiliary_client.OpenAI"):
|
||||
mock_nous.return_value = {"access_token": "***"}
|
||||
client, model = get_text_auxiliary_client("compression")
|
||||
# Config-first: model comes from config.yaml summary_model default,
|
||||
# but provider is forced to Nous via env var
|
||||
assert client is not None
|
||||
|
||||
def test_web_extract_task_override(self, monkeypatch):
|
||||
monkeypatch.setenv("AUXILIARY_WEB_EXTRACT_PROVIDER", "openrouter")
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
|
||||
with patch("agent.auxiliary_client.OpenAI"):
|
||||
client, model = get_text_auxiliary_client("web_extract")
|
||||
assert model == "google/gemini-3-flash-preview"
|
||||
|
||||
def test_task_direct_endpoint_from_config(self, monkeypatch, tmp_path):
|
||||
hermes_home = tmp_path / "hermes"
|
||||
hermes_home.mkdir(parents=True, exist_ok=True)
|
||||
@@ -979,8 +885,6 @@ class TestTaskSpecificOverrides:
|
||||
"""model:
|
||||
default: glm-5.1
|
||||
provider: opencode-go
|
||||
compression:
|
||||
summary_provider: auto
|
||||
"""
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
@@ -1039,25 +943,6 @@ model:
|
||||
"model": "gpt-5.4",
|
||||
}
|
||||
|
||||
def test_compression_summary_base_url_from_config(self, monkeypatch, tmp_path):
|
||||
"""compression.summary_base_url should produce a custom-endpoint client."""
|
||||
hermes_home = tmp_path / "hermes"
|
||||
hermes_home.mkdir(parents=True, exist_ok=True)
|
||||
(hermes_home / "config.yaml").write_text(
|
||||
"""compression:
|
||||
summary_provider: custom
|
||||
summary_model: glm-4.7
|
||||
summary_base_url: https://api.z.ai/api/coding/paas/v4
|
||||
"""
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
# Custom endpoints need an API key to build the client
|
||||
monkeypatch.setenv("OPENAI_API_KEY", "test-key")
|
||||
with patch("agent.auxiliary_client.OpenAI") as mock_openai:
|
||||
client, model = get_text_auxiliary_client("compression")
|
||||
assert model == "glm-4.7"
|
||||
assert mock_openai.call_args.kwargs["base_url"] == "https://api.z.ai/api/coding/paas/v4"
|
||||
|
||||
|
||||
class TestAuxiliaryMaxTokensParam:
|
||||
def test_codex_fallback_uses_max_tokens(self, monkeypatch):
|
||||
|
||||
@@ -273,18 +273,6 @@ class TestDefaultConfigShape:
|
||||
assert web["provider"] == "auto"
|
||||
assert web["model"] == ""
|
||||
|
||||
def test_compression_provider_default(self):
|
||||
from hermes_cli.config import DEFAULT_CONFIG
|
||||
compression = DEFAULT_CONFIG["compression"]
|
||||
assert "summary_provider" in compression
|
||||
assert compression["summary_provider"] == "auto"
|
||||
|
||||
def test_compression_base_url_default(self):
|
||||
from hermes_cli.config import DEFAULT_CONFIG
|
||||
compression = DEFAULT_CONFIG["compression"]
|
||||
assert "summary_base_url" in compression
|
||||
assert compression["summary_base_url"] is None
|
||||
|
||||
|
||||
# ── CLI defaults parity ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -12,17 +12,6 @@ def _isolate(tmp_path, monkeypatch):
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
for env_var in (
|
||||
"AUXILIARY_VISION_PROVIDER",
|
||||
"AUXILIARY_VISION_MODEL",
|
||||
"AUXILIARY_VISION_BASE_URL",
|
||||
"AUXILIARY_VISION_API_KEY",
|
||||
"CONTEXT_VISION_PROVIDER",
|
||||
"CONTEXT_VISION_MODEL",
|
||||
"CONTEXT_VISION_BASE_URL",
|
||||
"CONTEXT_VISION_API_KEY",
|
||||
):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
# Write a minimal config so load_config doesn't fail
|
||||
(hermes_home / "config.yaml").write_text("model:\n default: test-model\n")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user