chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
"""Unit tests for opensquilla.contrib.codetask.agent_config (pure assembly)."""
|
||||
|
||||
import tomllib
|
||||
|
||||
import pytest
|
||||
|
||||
from opensquilla.contrib.codetask import agent_config as agent_config_mod
|
||||
from opensquilla.contrib.codetask.agent_config import (
|
||||
OPERATOR_SECTIONS,
|
||||
AgentConfigError,
|
||||
build_per_run_agent_config,
|
||||
load_agent_config_bundle,
|
||||
user_config_payload,
|
||||
)
|
||||
|
||||
_TEMPLATE = {
|
||||
"workspace_strict": False,
|
||||
"sandbox": {"sandbox": False, "security_grading": False},
|
||||
"tools": {"deny": ["memory*"]},
|
||||
"meta_skill": {"enabled": False},
|
||||
"memory": {"flush_enabled": False},
|
||||
}
|
||||
|
||||
|
||||
def test_operator_sections_replace_template_wholesale():
|
||||
template = dict(_TEMPLATE, llm={"provider": "openrouter", "max_tokens": 32768})
|
||||
user = {
|
||||
"llm": {"provider": "deepseek", "model": "deepseek-chat"},
|
||||
"models": {"deepseek": {"deepseek-chat": {"context_window": 128000}}},
|
||||
}
|
||||
bundle = build_per_run_agent_config(template, user)
|
||||
# Replaced, not field-merged: the template's max_tokens must not leak
|
||||
# under the operator's provider.
|
||||
assert bundle.payload["llm"] == {"provider": "deepseek", "model": "deepseek-chat"}
|
||||
assert bundle.payload["models"]["deepseek"]["deepseek-chat"]["context_window"] == 128000
|
||||
# Template policy survives untouched.
|
||||
assert bundle.payload["tools"]["deny"] == ["memory*"]
|
||||
assert bundle.payload["meta_skill"]["enabled"] is False
|
||||
|
||||
|
||||
def test_operator_absent_sections_are_dropped_from_template():
|
||||
template = dict(_TEMPLATE, llm={"provider": "openrouter"}, squilla_router={"enabled": True})
|
||||
bundle = build_per_run_agent_config(template, {})
|
||||
for section in OPERATOR_SECTIONS:
|
||||
assert section not in bundle.payload, section
|
||||
|
||||
|
||||
def test_primary_api_key_moves_to_child_env():
|
||||
user = {"llm": {"provider": "deepseek", "model": "deepseek-chat", "api_key": "sk-secret"}}
|
||||
bundle = build_per_run_agent_config(dict(_TEMPLATE), user)
|
||||
assert "api_key" not in bundle.payload["llm"]
|
||||
assert bundle.child_env == {"OPENSQUILLA_LLM_API_KEY": "sk-secret"}
|
||||
# The source payload is not mutated.
|
||||
assert user["llm"]["api_key"] == "sk-secret"
|
||||
|
||||
|
||||
def test_empty_api_key_is_not_stripped():
|
||||
# An explicitly empty api_key must stay in the written [llm] (the operator
|
||||
# resolves via api_key_env); dropping it would let a stale inherited
|
||||
# OPENSQUILLA_LLM_API_KEY fill the field in the child.
|
||||
user = {"llm": {"provider": "deepseek", "model": "m", "api_key": "", "api_key_env": "DS_KEY"}}
|
||||
bundle = build_per_run_agent_config(dict(_TEMPLATE), user)
|
||||
assert bundle.payload["llm"]["api_key"] == ""
|
||||
assert bundle.child_env == {}
|
||||
|
||||
|
||||
def test_profile_api_keys_stay_in_payload():
|
||||
# Profile keys have no env transport channel; they ride in the 0600 file.
|
||||
user = {
|
||||
"llm": {"provider": "deepseek", "model": "deepseek-chat"},
|
||||
"llm_profiles": {"moonshot": {"api_key": "sk-profile"}},
|
||||
}
|
||||
bundle = build_per_run_agent_config(dict(_TEMPLATE), user)
|
||||
assert bundle.payload["llm_profiles"]["moonshot"]["api_key"] == "sk-profile"
|
||||
assert bundle.child_env == {}
|
||||
|
||||
|
||||
def test_llm_ensemble_is_never_carried():
|
||||
user = {
|
||||
"llm": {"provider": "deepseek", "model": "deepseek-chat"},
|
||||
"llm_ensemble": {"enabled": True},
|
||||
}
|
||||
bundle = build_per_run_agent_config(dict(_TEMPLATE), user)
|
||||
assert "llm_ensemble" not in bundle.payload
|
||||
|
||||
|
||||
def test_mismatched_tier_profile_heals_in_merged_config():
|
||||
# A tier_profile that no longer matches llm.provider used to hard-fail
|
||||
# validation; the config-migration layer now clears it (the same healing
|
||||
# gateway boot applies to hand-edited configs), so the merged per-run
|
||||
# config builds cleanly with the profile pointer dropped instead of
|
||||
# failing at subagent boot.
|
||||
user = {
|
||||
"llm": {"provider": "deepseek", "model": "deepseek-chat"},
|
||||
"squilla_router": {"enabled": True, "tier_profile": "moonshot"},
|
||||
}
|
||||
bundle = build_per_run_agent_config(
|
||||
dict(_TEMPLATE), user, user_config_path="/x/config.toml"
|
||||
)
|
||||
assert "tier_profile" not in bundle.payload.get("squilla_router", {})
|
||||
|
||||
|
||||
def test_user_config_payload_explicit_env_path(monkeypatch, tmp_path):
|
||||
cfg = tmp_path / "operator.toml"
|
||||
cfg.write_text('[llm]\nprovider = "deepseek"\nmodel = "m"\n', encoding="utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", str(cfg))
|
||||
payload, source = user_config_payload()
|
||||
assert payload["llm"]["provider"] == "deepseek"
|
||||
assert source == str(cfg)
|
||||
|
||||
|
||||
def test_user_config_payload_explicit_env_path_missing_is_sole_candidate(
|
||||
monkeypatch, tmp_path
|
||||
):
|
||||
# Mirrors GatewayConfig.load: an explicit path never falls back to the
|
||||
# cwd/home chain.
|
||||
(tmp_path / "state").mkdir()
|
||||
(tmp_path / "state" / "config.toml").write_text('[llm]\nprovider = "moonshot"\n', "utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_STATE_DIR", str(tmp_path / "state"))
|
||||
monkeypatch.setenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", str(tmp_path / "missing.toml"))
|
||||
payload, source = user_config_payload()
|
||||
assert payload == {}
|
||||
assert source is None
|
||||
|
||||
|
||||
def test_user_config_payload_falls_back_to_home_config(monkeypatch, tmp_path):
|
||||
monkeypatch.delenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", raising=False)
|
||||
monkeypatch.chdir(tmp_path) # no ./opensquilla.toml here
|
||||
home = tmp_path / "state"
|
||||
home.mkdir()
|
||||
(home / "config.toml").write_text('[llm]\nprovider = "deepseek"\nmodel = "m"\n', "utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_STATE_DIR", str(home))
|
||||
payload, source = user_config_payload()
|
||||
assert payload["llm"]["provider"] == "deepseek"
|
||||
assert source == str(home / "config.toml")
|
||||
|
||||
|
||||
def test_user_config_payload_cwd_toml_wins_over_home(monkeypatch, tmp_path):
|
||||
monkeypatch.delenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", raising=False)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
(tmp_path / "opensquilla.toml").write_text(
|
||||
'[llm]\nprovider = "deepseek"\nmodel = "m"\n', "utf-8"
|
||||
)
|
||||
home = tmp_path / "state"
|
||||
home.mkdir()
|
||||
(home / "config.toml").write_text('[llm]\nprovider = "moonshot"\nmodel = "k"\n', "utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_STATE_DIR", str(home))
|
||||
payload, _source = user_config_payload()
|
||||
assert payload["llm"]["provider"] == "deepseek"
|
||||
|
||||
|
||||
def test_unparseable_operator_config_raises_actionably(monkeypatch, tmp_path):
|
||||
cfg = tmp_path / "broken.toml"
|
||||
cfg.write_text("[llm\nprovider=", encoding="utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", str(cfg))
|
||||
with pytest.raises(AgentConfigError) as exc:
|
||||
user_config_payload()
|
||||
assert str(cfg) in str(exc.value)
|
||||
|
||||
|
||||
def test_override_env_skips_inheritance(monkeypatch, tmp_path):
|
||||
override = tmp_path / "override.toml"
|
||||
override.write_text(
|
||||
'[llm]\nprovider = "deepseek"\nmodel = "deepseek-chat"\napi_key = "sk-keep"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
operator = tmp_path / "operator.toml"
|
||||
operator.write_text('[llm]\nprovider = "moonshot"\nmodel = "kimi-k2.6"\n', "utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_CODETASK_AGENT_CONFIG", str(override))
|
||||
monkeypatch.setenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", str(operator))
|
||||
bundle = load_agent_config_bundle()
|
||||
assert bundle.payload["llm"]["provider"] == "deepseek"
|
||||
assert bundle.payload["llm"]["api_key"] == "sk-keep" # full authority, untouched
|
||||
assert bundle.child_env == {}
|
||||
|
||||
|
||||
def test_blank_override_env_is_treated_as_unset(monkeypatch, tmp_path):
|
||||
# A quoted-empty OPENSQUILLA_CODETASK_AGENT_CONFIG must not be taken as a
|
||||
# path (which would hard-fail every run reading a whitespace filename);
|
||||
# inheritance proceeds normally.
|
||||
operator = tmp_path / "operator.toml"
|
||||
operator.write_text('[llm]\nprovider = "deepseek"\nmodel = "m"\n', encoding="utf-8")
|
||||
monkeypatch.setenv("OPENSQUILLA_CODETASK_AGENT_CONFIG", " ")
|
||||
monkeypatch.setenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", str(operator))
|
||||
bundle = load_agent_config_bundle()
|
||||
assert bundle.payload["llm"]["provider"] == "deepseek"
|
||||
|
||||
|
||||
def test_bundled_template_pins_no_provider_sections():
|
||||
# The shipped template is a run-policy file; pinning [llm]/[squilla_router]
|
||||
# there would shadow the operator's provider (issue #541 regression guard).
|
||||
from opensquilla.contrib.codetask.config import _DATA_DIR
|
||||
|
||||
template = tomllib.loads(
|
||||
(_DATA_DIR / "agent_config" / "config.toml").read_text(encoding="utf-8")
|
||||
)
|
||||
for section in OPERATOR_SECTIONS:
|
||||
assert section not in template, section
|
||||
for section in ("tools", "sandbox", "meta_skill", "memory"):
|
||||
assert section in template, section
|
||||
|
||||
|
||||
def test_merged_payload_round_trips_through_gateway_config(monkeypatch, tmp_path):
|
||||
"""End to end: the assembled payload for a deepseek operator loads as a
|
||||
deepseek config with an auto-aligned router tier profile."""
|
||||
operator = tmp_path / "operator.toml"
|
||||
operator.write_text(
|
||||
'[llm]\nprovider = "deepseek"\nmodel = "deepseek-chat"\n', encoding="utf-8"
|
||||
)
|
||||
monkeypatch.setenv("OPENSQUILLA_GATEWAY_CONFIG_PATH", str(operator))
|
||||
monkeypatch.delenv("OPENSQUILLA_CODETASK_AGENT_CONFIG", raising=False)
|
||||
bundle = load_agent_config_bundle()
|
||||
|
||||
from opensquilla.gateway.config import GatewayConfig
|
||||
|
||||
conf = GatewayConfig(**bundle.payload)
|
||||
assert conf.llm.provider == "deepseek"
|
||||
assert conf.llm.model == "deepseek-chat"
|
||||
assert conf.squilla_router.tier_profile in ("deepseek", None)
|
||||
|
||||
|
||||
def test_agent_config_path_still_honors_override(monkeypatch, tmp_path):
|
||||
# Regression guard for the existing escape-hatch contract.
|
||||
custom = tmp_path / "custom.toml"
|
||||
monkeypatch.setenv("OPENSQUILLA_CODETASK_AGENT_CONFIG", str(custom))
|
||||
assert agent_config_mod.agent_config_path() == custom
|
||||
Reference in New Issue
Block a user