from __future__ import annotations import json import os import sys import tempfile import unittest from pathlib import Path SERVICE_ROOT = Path(__file__).resolve().parents[1] BACKEND_ROOT = SERVICE_ROOT.parent if str(SERVICE_ROOT) not in sys.path: sys.path.insert(0, str(SERVICE_ROOT)) from inference.config import ( # noqa: E402 InferenceManifestValidationError, default_main_model, load_inference_manifest, reset_inference_manifest_cache, resolve_manifest_path, resolve_model, resolve_timeout_seconds, ) CANONICAL_MANIFEST = BACKEND_ROOT / "config" / "inference.manifest.json" class InferenceConfigTests(unittest.TestCase): def setUp(self) -> None: reset_inference_manifest_cache() self._old_manifest_path = os.environ.pop("INFERENCE_MANIFEST_PATH", None) self._old_model = os.environ.pop("TABULAR_AGENT_MODEL", None) self._old_timeout = os.environ.pop("TABULAR_AGENT_TIMEOUT_SECONDS", None) def tearDown(self) -> None: reset_inference_manifest_cache() if self._old_manifest_path is not None: os.environ["INFERENCE_MANIFEST_PATH"] = self._old_manifest_path else: os.environ.pop("INFERENCE_MANIFEST_PATH", None) if self._old_model is not None: os.environ["TABULAR_AGENT_MODEL"] = self._old_model else: os.environ.pop("TABULAR_AGENT_MODEL", None) if self._old_timeout is not None: os.environ["TABULAR_AGENT_TIMEOUT_SECONDS"] = self._old_timeout else: os.environ.pop("TABULAR_AGENT_TIMEOUT_SECONDS", None) def test_loads_canonical_manifest(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) manifest = load_inference_manifest() self.assertEqual(manifest["defaults"]["model"], "gemini-3.5-flash") def test_same_default_model_as_typescript(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) self.assertEqual(default_main_model(), "gemini-3.5-flash") def test_invalid_manifest_fails_validation(self) -> None: with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as handle: json.dump({"defaults": {"provider": "proxy-openai"}}, handle) invalid_path = handle.name try: with self.assertRaises(InferenceManifestValidationError): load_inference_manifest(Path(invalid_path)) finally: os.unlink(invalid_path) def test_request_model_id_precedence(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) os.environ["TABULAR_AGENT_MODEL"] = "gemini-3-flash" resolved = resolve_model( request_model_id="gemini-3.5-flash", feature_id="python-review", ) self.assertEqual(resolved, "gemini-3.5-flash") def test_env_model_precedence_over_feature(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) os.environ["TABULAR_AGENT_MODEL"] = "gemini-3-flash" resolved = resolve_model(request_model_id=None, feature_id="python-review") self.assertEqual(resolved, "gemini-3-flash") def test_feature_precedence_over_defaults(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) resolved = resolve_model(request_model_id=None, feature_id="python-review") self.assertEqual(resolved, "gemini-3.5-flash") def test_python_chat_falls_back_to_python_review_model(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) resolved = resolve_model(request_model_id=None, feature_id="python-chat") self.assertEqual(resolved, "gemini-3.5-flash") def test_legacy_pro_alias_falls_through(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) resolved = resolve_model(request_model_id="pro", feature_id="python-review") self.assertEqual(resolved, "gemini-3.5-flash") def test_timeout_from_manifest_with_env_override(self) -> None: os.environ["INFERENCE_MANIFEST_PATH"] = str(CANONICAL_MANIFEST) self.assertEqual(resolve_timeout_seconds("python-review"), 180.0) os.environ["TABULAR_AGENT_TIMEOUT_SECONDS"] = "42" reset_inference_manifest_cache() self.assertEqual(resolve_timeout_seconds("python-review"), 42.0) def test_host_development_manifest_fallback_path(self) -> None: reset_inference_manifest_cache() os.environ.pop("INFERENCE_MANIFEST_PATH", None) self.assertEqual(resolve_manifest_path(), CANONICAL_MANIFEST) if __name__ == "__main__": unittest.main()