diff --git a/aider/models.py b/aider/models.py index d8ead7452..4816fe751 100644 --- a/aider/models.py +++ b/aider/models.py @@ -16,7 +16,6 @@ from PIL import Image from aider import urls from aider.dump import dump # noqa: F401 from aider.llm import AIDER_APP_NAME, AIDER_SITE_URL, litellm -from aider.utils import safe_read_json, safe_write_json DEFAULT_MODEL_NAME = "gpt-4o" ANTHROPIC_BETA_HEADER = "max-tokens-3-5-sonnet-2024-07-15,prompt-caching-2024-07-31" @@ -427,58 +426,44 @@ MODEL_SETTINGS = [ def get_model_info(model): - if litellm._lazy_module: - # Do it the slow way... - try: - return litellm.get_model_info(model) - except Exception: - return dict() + if not litellm._lazy_module: + cache_dir = Path.home() / ".aider" / "caches" + cache_file = cache_dir / "model_prices_and_context_window.json" + cache_dir.mkdir(parents=True, exist_ok=True) - cache_dir = Path.home() / ".aider" / "caches" - cache_file = cache_dir / "model_prices_and_context_window.json" - cache_dir.mkdir(parents=True, exist_ok=True) + current_time = time.time() + cache_age = ( + current_time - cache_file.stat().st_mtime if cache_file.exists() else float("inf") + ) - current_time = time.time() - cache_age = current_time - cache_file.stat().st_mtime if cache_file.exists() else float("inf") - - if cache_file.exists() and cache_age < 86400: # 86400 seconds = 1 day - content = safe_read_json(cache_file) - if content: - info = content.get(model) - if info: + if cache_age < 60 * 60 * 24: + try: + content = json.loads(cache_file.read_text()) + info = content.get(model, dict()) return info + except Exception as ex: + print(str(ex)) - # If cache doesn't exist or is old, fetch from GitHub - try: import requests url = ( "https://raw.githubusercontent.com/BerriAI/litellm/main/" "model_prices_and_context_window.json" ) - response = requests.get(url, timeout=5) - if response.status_code == 200: - content = response.json() - safe_write_json(cache_file, content) - info = content.get(model) - if info: - return info - except Exception: - # If fetching from GitHub fails, fall back to local resource try: - with importlib.resources.open_text( - "litellm", "model_prices_and_context_window_backup.json" - ) as f: - content = json.load(f) - info = content.get(model) - if info: - return info - except Exception: - pass # If there's any error, fall back to the slow way + response = requests.get(url, timeout=5) + if response.status_code == 200: + content = response.json() + cache_file.write_text(json.dumps(content, indent=4)) + info = content.get(model, dict()) + return info + except Exception as ex: + print(str(ex)) # If all else fails, do it the slow way... try: - return litellm.get_model_info(model) + info = litellm.get_model_info(model) + return info except Exception: return dict() @@ -490,7 +475,6 @@ class Model(ModelSettings): self.weak_model = None self.info = self.get_model_info(model) - dump(self.info) # Are all needed keys/params available? res = self.validate_environment() diff --git a/aider/utils.py b/aider/utils.py index 43aaaa842..5c2d6c7bd 100644 --- a/aider/utils.py +++ b/aider/utils.py @@ -92,23 +92,6 @@ def is_image_file(file_name): return any(file_name.endswith(ext) for ext in IMAGE_EXTENSIONS) -def safe_read_json(file_path): - try: - with open(file_path, "r") as f: - return json.load(f) - except Exception: - return None - - -def safe_write_json(file_path, data): - try: - with open(file_path, "w") as f: - json.dump(data, f) - return True - except Exception: - return False - - def safe_abs_path(res): "Gives an abs path, which safely returns a full (not 8.3) windows path" res = Path(res).resolve() diff --git a/tests/basic/test_models.py b/tests/basic/test_models.py index 3706a79ea..8b3da4af1 100644 --- a/tests/basic/test_models.py +++ b/tests/basic/test_models.py @@ -29,49 +29,6 @@ class TestModels(unittest.TestCase): model = Model("gpt-4-0613") self.assertEqual(model.info["max_input_tokens"], 8 * 1024) - @patch("aider.models.litellm._lazy_module", False) - @patch("aider.models.Path.home") - @patch("aider.models.Path.stat") - @patch("aider.models.safe_read_json") - @patch("aider.models.safe_write_json") - @patch("requests.get") - @patch("aider.models.Path.mkdir") - def test_get_model_info( - self, mock_mkdir, mock_get, mock_write_json, mock_read_json, mock_stat, mock_home - ): - # Setup - mock_home.return_value = Path("/mock/home") - mock_stat.return_value = unittest.mock.Mock(st_mtime=time.time() - 86400 * 2) # 2 days old - mock_mkdir.return_value = None # Ensure mkdir doesn't raise an exception - - # Test case 1: Cache exists and is fresh - mock_read_json.return_value = {"test_model": {"info": "cached"}} - mock_stat.return_value.st_mtime = time.time() - 3600 # 1 hour old - self.assertEqual(get_model_info("test_model"), {"info": "cached"}) - - # Test case 2: Cache doesn't exist or is old, GitHub fetch succeeds - mock_read_json.return_value = None - mock_get.return_value.status_code = 200 - mock_get.return_value.json.return_value = {"test_model": {"info": "from_github"}} - self.assertEqual(get_model_info("test_model"), {"info": "from_github"}) - - # Test case 3: Cache doesn't exist, GitHub fetch fails, fallback to local resource - mock_get.return_value.status_code = 404 - with patch("importlib.resources.open_text") as mock_open_text: - mock_open_text.return_value.__enter__.return_value.read.return_value = json.dumps( - {"test_model": {"info": "local_backup"}} - ) - self.assertEqual(get_model_info("test_model"), {"info": "local_backup"}) - - # Test case 4: All previous methods fail, fallback to litellm.get_model_info - mock_open_text.side_effect = Exception("Resource not found") - with patch("aider.models.litellm.get_model_info") as mock_litellm_get_model_info: - mock_litellm_get_model_info.return_value = {"info": "from_litellm"} - self.assertEqual(get_model_info("test_model"), {"info": "from_litellm"}) - - # Test case 5: Everything fails - mock_litellm_get_model_info.side_effect = Exception("LiteLLM failed") - self.assertEqual(get_model_info("test_model"), {}) if __name__ == "__main__":