-
Notifications
You must be signed in to change notification settings - Fork 1
refactor: switch from api.json to models.dev/models.json #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,10 @@ | ||
| import json | ||
| import time | ||
| import urllib.request | ||
| from urllib.parse import urlparse | ||
|
|
||
| from ..config import ( | ||
| CONFIG_DIR, | ||
| DEFAULT_PROVIDERS, | ||
| REASONING_EFFORT_LEVELS, | ||
| client, | ||
| config, | ||
|
|
@@ -13,25 +13,6 @@ | |
| from .display.picker import select_from_list | ||
|
|
||
|
|
||
| def _get_provider_hint(model_id: str) -> str | None: | ||
| for provider_id, prefixes in DEFAULT_PROVIDERS.items(): | ||
| if model_id.startswith(tuple(prefixes)): | ||
| return provider_id | ||
| return None | ||
|
|
||
|
|
||
| def _get_limit_for_provider( | ||
| cache: dict[str, dict], provider_id: str, model_id: str, key: str | ||
| ) -> int | None: | ||
| provider = cache.get(provider_id) | ||
| if not provider: | ||
| return None | ||
| model = (provider.get("models") or {}).get(model_id) | ||
| if not model: | ||
| return None | ||
| return (model.get("limit") or {}).get(key) | ||
|
|
||
|
|
||
| class _ModelInfo: | ||
| def __init__(self) -> None: | ||
| self._cache_path = CONFIG_DIR / "models.json" | ||
|
|
@@ -48,41 +29,27 @@ def _load_cache(self) -> dict[str, dict]: | |
| return self._cache | ||
|
|
||
| def get_best_limit(self, model_id: str, key: str) -> int | None: | ||
| """Return the maximum value for *key* (e.g. 'context' or 'output') across all providers for a given model.""" | ||
| """Return the value for *key* (e.g. 'context' or 'output') for a given model.""" | ||
| cache = self._load_cache() | ||
|
|
||
| # 1. Try with user-specified provider | ||
| user_provider = config.get_provider() | ||
| if user_provider: | ||
| best = _get_limit_for_provider(cache, user_provider, model_id, key) | ||
| if best is not None: | ||
| return best | ||
|
|
||
| # 2. Fall back to prefix matching using DEFAULT_PROVIDERS | ||
| provider_hint = _get_provider_hint(model_id) | ||
| if provider_hint and provider_hint != user_provider: | ||
| best = _get_limit_for_provider(cache, provider_hint, model_id, key) | ||
| if best is not None: | ||
| return best | ||
|
|
||
| # 3. Fall back to searching across ALL providers in the catalog | ||
| best = None | ||
| for provider_id, provider in cache.items(): | ||
| if provider_id in (user_provider, provider_hint): | ||
| continue | ||
| model = (provider.get("models") or {}).get(model_id) | ||
| if model is None: | ||
| continue | ||
| value = (model.get("limit") or {}).get(key) | ||
| if value is not None and (best is None or value > best): | ||
| best = value | ||
| return best | ||
| model = cache.get(model_id) | ||
| if model is None: | ||
| for full_key, data in cache.items(): | ||
| if full_key.split("/")[-1] == model_id: | ||
| model = data | ||
| break | ||
| if model is None: | ||
| return None | ||
| return (model.get("limit") or {}).get(key) | ||
|
|
||
| def refresh_cache(self) -> None: | ||
| """Fetch the latest model data from the remote API and update the local cache.""" | ||
| if self._cache_path.exists(): | ||
| age = time.time() - self._cache_path.stat().st_mtime | ||
| if age < 3600: | ||
| return | ||
|
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the cache file exists but is empty or contains invalid JSON, if self._cache_path.exists() and self._load_cache():
age = time.time() - self._cache_path.stat().st_mtime
if age < 3600:
return
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The age check gates the entire fetch unconditionally on file existence and age. If the cache file is corrupted (invalid JSON), |
||
| try: | ||
| req = urllib.request.Request( | ||
| "https://models.dev/api.json", | ||
| "https://models.dev/models.json", | ||
| headers={"User-Agent": "Mozilla/5.0"}, | ||
| ) | ||
| with urllib.request.urlopen(req, timeout=5) as resp: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The loop returns the first entry whose
full_key.split("/")[-1]equalsmodel_id, and the order depends on JSON parse order. If two providers list a model with the same short name (e.g.,provider-a/deepseek-v4-flashandprovider-b/deepseek-v4-flash), the context/output limits from whichever entry appears first inmodels.jsonare returned, regardless of which provider is actually serving the model. Consider logging a warning or preferring a well-known provider when a collision is detected.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!