Skip to content

refactor: switch from api.json to models.dev/models.json#74

Merged
kowyo merged 1 commit into
mainfrom
chore/switch-to-models-dev-json
Jun 13, 2026
Merged

refactor: switch from api.json to models.dev/models.json#74
kowyo merged 1 commit into
mainfrom
chore/switch-to-models-dev-json

Conversation

@kowyo

@kowyo kowyo commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Description

Switch model data source from the old provider-centric api.json format to the new flat models.dev/models.json.

Changes:

  • config.py: Remove DEFAULT_PROVIDERS, get_provider(), and provider-related fields — no longer needed since models.dev/models.json uses model_id as the top-level key instead of grouping by provider.
  • models.py: Replace https://models.dev/api.json URL with https://models.dev/models.json; simplify get_best_limit() to work with flat model data; add 1-hour TTL cache to refresh_cache() to avoid unnecessary network requests; add short-name fallback lookup for model IDs returned by the Anthropic API (e.g. deepseek-v4-flashdeepseek/deepseek-v4-flash).
  • docs/config.md: Remove provider field documentation and related behavior description.

Type of change

  • Refactor

Test

  • Verified that get_best_limit() correctly resolves both full keys (deepseek/deepseek-v4-flash) and short names (deepseek-v4-flash)
  • Verified that cache TTL skips network request when file is younger than 1 hour
  • Python syntax check passed

Assisted-by: mini-agent:deepseek-v4-flash

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes provider-specific configuration and lookup logic, simplifies the completion menu anchoring, and introduces a new FileCompleter that uses fd to suggest file and folder paths when typing @. Feedback on the changes includes a critical fix for a Python syntax error in the exception handling of FileCompleter and a recommendation to improve the robustness of the cache refresh logic when the cache file is empty or invalid.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

text=True,
timeout=2.0,
)
except subprocess.TimeoutExpired, FileNotFoundError, OSError:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This line contains a syntax error in Python 3. Multiple exceptions in an except clause must be enclosed in parentheses. Otherwise, it will raise a SyntaxError at runtime.

Suggested change
except subprocess.TimeoutExpired, FileNotFoundError, OSError:
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):

Comment on lines +46 to +49
if self._cache_path.exists():
age = time.time() - self._cache_path.stat().st_mtime
if age < 3600:
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the cache file exists but is empty or contains invalid JSON, _load_cache() will return an empty dictionary, but refresh_cache() will still skip the refresh if the file was modified less than an hour ago. To make this more robust, we should only skip the refresh if the cache was successfully loaded with data.

        if self._cache_path.exists() and self._load_cache():
            age = time.time() - self._cache_path.stat().st_mtime
            if age < 3600:
                return

Replace the old provider-centric model catalog with the new flat
models.dev format. Remove DEFAULT_PROVIDERS, get_provider(), and
related provider lookup logic since the new JSON uses model_id as
the top-level key instead of grouping by provider.

Also add 1-hour TTL cache for refresh_cache() and short-name
fallback in get_best_limit() to handle Anthropic API model IDs
(e.g. deepseek-v4-flash -> deepseek/deepseek-v4-flash).

Assisted-by: mini-agent:deepseek-v4-flash
@kowyo kowyo force-pushed the chore/switch-to-models-dev-json branch from 5e0dadd to 659a37e Compare June 10, 2026 03:34
@kowyo kowyo marked this pull request as ready for review June 13, 2026 12:20

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Re-trigger cubic

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR migrates the model metadata source from the provider-grouped api.json format to the flat models.dev/models.json format, removing the provider abstraction layer entirely.

  • models.py: URL updated to models.json; get_best_limit() simplified to a direct key lookup with a short-name fallback (splits on /); a 1-hour TTL guard added to refresh_cache() to skip unnecessary network fetches.
  • config.py: DEFAULT_PROVIDERS, get_provider(), and the _provider/_provider_loaded fields removed; no remaining callsites found.
  • docs/config.md: provider field entry and the default-provider prefix table removed, keeping docs in sync with the simplified config.

Confidence Score: 4/5

Safe to merge; the refactor correctly removes the provider layer and the new flat-lookup logic works for the described use cases.

The core migration is clean and complete — no dangling references to the removed API were found. The two edge cases (corrupt cache persisting within TTL, and ambiguous short-name collisions across providers) are unlikely to trigger in practice but represent silent failure modes worth addressing in a follow-up.

src/mini_agent/cli/models.py — specifically the TTL guard in refresh_cache() and the short-name fallback loop in get_best_limit().

Important Files Changed

Filename Overview
src/mini_agent/cli/models.py URL switched to models.json, get_best_limit simplified to flat lookup with short-name fallback, 1-hour TTL guard added to refresh_cache; two minor edge cases around corrupt cache bypassing re-download and ambiguous short-name collisions.
src/mini_agent/config.py Cleanly removes DEFAULT_PROVIDERS, get_provider(), and related private fields; no remaining references to the removed API anywhere in the codebase.
docs/config.md Removes provider field documentation and the default-provider prefix table; docs now accurately reflect the simplified config surface.

Sequence Diagram

sequenceDiagram
    participant User
    participant prompt_model
    participant refresh_cache
    participant _load_cache
    participant models_dev as models.dev/models.json

    User->>prompt_model: /model command
    prompt_model->>refresh_cache: refresh_cache()
    alt "cache file exists AND age < 1 hour"
        refresh_cache-->>prompt_model: return (skip network)
    else cache missing or stale
        refresh_cache->>models_dev: GET models.json
        models_dev-->>refresh_cache: "flat {model_id: {limit: {...}}}"
        refresh_cache->>refresh_cache: write to ~/.mini-agent/models.json
        refresh_cache->>refresh_cache: "self._cache = data"
    end
    prompt_model->>prompt_model: fetch_models() via SDK/API
    prompt_model->>_load_cache: get_best_limit(model_id, key)
    alt full key match
        _load_cache-->>prompt_model: return limit value
    else short-name fallback
        _load_cache->>_load_cache: "scan cache for key.split("/")[-1] == model_id"
        _load_cache-->>prompt_model: return first match's limit value
    end
Loading

Reviews (1): Last reviewed commit: "refactor: switch from api.json to models..." | Re-trigger Greptile

Comment on lines +46 to +49
if self._cache_path.exists():
age = time.time() - self._cache_path.stat().st_mtime
if age < 3600:
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 TTL skips refresh even for corrupt cache files

The age check gates the entire fetch unconditionally on file existence and age. If the cache file is corrupted (invalid JSON), _load_cache() catches the JSONDecodeError and silently returns {}, but refresh_cache() will not attempt to re-download within the 1-hour window because the file's mtime is recent. A user would see stale empty limits for up to an hour with no feedback.

Comment on lines +35 to +39
if model is None:
for full_key, data in cache.items():
if full_key.split("/")[-1] == model_id:
model = data
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Short-name fallback can silently return data for the wrong provider

The loop returns the first entry whose full_key.split("/")[-1] equals model_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-flash and provider-b/deepseek-v4-flash), the context/output limits from whichever entry appears first in models.json are 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!

@kowyo kowyo merged commit fadee4a into main Jun 13, 2026
27 checks passed
@kowyo kowyo deleted the chore/switch-to-models-dev-json branch June 13, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant