import json import os import tempfile import unittest from pathlib import Path from unittest import mock from approach.config import ( OPENROUTER_BASE_URL, get_model_profile, sanitize_manifest, validate_model_available, ) from approach.providers import EncodedImage, OpenAICompatibleChatClient, make_multimodal_content class ConfigProviderTests(unittest.TestCase): def test_default_profile_uses_openrouter(self): profile = get_model_profile("default") self.assertEqual(profile.provider, "openrouter") self.assertEqual(profile.model, "openai/gpt-5.6-sol") self.assertEqual(profile.base_url, OPENROUTER_BASE_URL) self.assertEqual(profile.api_key_env, "OPENROUTER_API_KEY") def test_aliases_keep_paper_profiles_available(self): self.assertEqual(get_model_profile("gpt4v").model, "openai/gpt-4o-2024-08-06") self.assertEqual(get_model_profile("claude35sonnet").model, "anthropic/claude-3.5-sonnet") self.assertEqual(get_model_profile("gemini15pro").model, "google/gemini-pro-1.5") def test_json_config_override_without_yaml_dependency(self): with tempfile.TemporaryDirectory() as tmpdir: path = Path(tmpdir) / "profiles.json" path.write_text( json.dumps( { "profiles": { "default": { "provider": "openrouter", "model": "custom/model", "base_url": OPENROUTER_BASE_URL, "api_key_env": "CUSTOM_KEY", } } } ) ) profile = get_model_profile("default", str(path)) self.assertEqual(profile.model, "custom/model") self.assertEqual(profile.api_key_env, "CUSTOM_KEY") def test_model_catalog_validation_is_dependency_injected(self): profile = get_model_profile("default") self.assertTrue(validate_model_available(profile, [{"id": "openai/gpt-5.6-sol"}])) with self.assertRaises(ValueError): validate_model_available(profile, [{"id": "other/model"}]) def test_multimodal_content_supports_multiple_images(self): content = make_multimodal_content( "check", ["aaa", EncodedImage(data="bbb", media_type="image/png")], ) self.assertEqual(content[0], {"type": "text", "text": "check"}) self.assertEqual(len(content), 3) self.assertEqual(content[1]["image_url"]["url"], "data:image/jpeg;base64,aaa") self.assertEqual(content[2]["image_url"]["url"], "data:image/png;base64,bbb") def test_client_posts_openai_compatible_payload(self): profile = get_model_profile("default") response = mock.Mock() response.json.return_value = { "model": profile.model, "choices": [{"message": {"content": "{\"ok\": true}"}}], } post = mock.Mock(return_value=response) with mock.patch.dict(os.environ, {"OPENROUTER_API_KEY": "test-token"}): client = OpenAICompatibleChatClient(profile, post=post) payload = client.build_payload( "verify", ["image-a", "image-b"], response_format={"type": "json_schema", "json_schema": {"name": "x"}}, ) result = client.complete_json( "verify", ["image-a", "image-b"], response_format={"type": "json_schema", "json_schema": {"name": "x"}}, ) self.assertEqual(payload["model"], "openai/gpt-5.6-sol") self.assertEqual(result, {"ok": True}) self.assertEqual( post.call_args.args[0], "https://openrouter.ai/api/v1/chat/completions", ) self.assertEqual(post.call_args.kwargs["headers"]["Authorization"], "Bearer test-token") self.assertIn("response_format", post.call_args.kwargs["json"]) def test_client_supports_multi_turn_json_requests(self): profile = get_model_profile("paper_claude") response = mock.Mock() response.json.return_value = { "choices": [{"message": {"content": '{"objects": ["button"]}'}}] } post = mock.Mock(return_value=response) messages = [ {"role": "user", "content": "list objects"}, {"role": "assistant", "content": '{"objects": ["button"]}'}, {"role": "user", "content": "filter interactable objects"}, ] with mock.patch.dict(os.environ, {"OPENROUTER_API_KEY": "test-token"}): result = OpenAICompatibleChatClient(profile, post=post).complete_json_messages( messages ) self.assertEqual(result, {"objects": ["button"]}) self.assertEqual(post.call_args.kwargs["json"]["messages"], messages) def test_manifest_never_records_api_key_value(self): profile = get_model_profile("default") manifest = sanitize_manifest( profile, {"model": "openai/gpt-5.6-sol"}, {"model": "openai/gpt-5.6-sol", "usage": {"total_tokens": 12}}, ) self.assertEqual(manifest["api_key_env"], "OPENROUTER_API_KEY") self.assertNotIn("sk-", json.dumps(manifest)) if __name__ == "__main__": unittest.main()