Skip to content

Derive the model catalog from pydantic-ai and seed new evaluator versions - #16

Merged
duncankmckinnon merged 1 commit into
mainfrom
model-catalog-and-version-seeding
Aug 13, 2026
Merged

Derive the model catalog from pydantic-ai and seed new evaluator versions#16
duncankmckinnon merged 1 commit into
mainfrom
model-catalog-and-version-seeding

Conversation

@duncankmckinnon

Copy link
Copy Markdown
Owner

Two related changes to evaluator version authoring.

Open the model catalog

MODEL_CATALOG was five hand-maintained strings while the Pydantic AI Gateway serves far more. It is now derived: model_catalog() filters pydantic-ai's public known_model_names() through the existing GATEWAY_ROUTES, yielding 160 models that track whatever version is pinned.

Route Models
gateway/openai 62
gateway/bedrock 55
gateway/anthropic 14
gateway/google-cloud 12
gateway/google 12
gateway/groq 5

Filtering through GATEWAY_ROUTES rather than a bare gateway/ prefix means every suggestion is a string validate_model_string accepts, so the two cannot drift.

The catalog is type-ahead only, not a whitelist. The model field is now an <input> + <datalist> instead of a <select>, so any well-formed gateway/<route>:<name> is enterable and the Gateway stays the authority on what actually exists. validate_model_string was already shape-only and is unchanged. This matters because the library's list reflects what the pinned pydantic-ai knows, not what a given account is entitled to — it can be both too wide and too narrow.

Seed new versions from the one on screen

"New version" opened a blank editor, discarding all the prior text. It now prefills from the version being viewed, mirroring the server's existing copy_version (the frozen "save as new version" path), including carrying version_name over verbatim — the server does not constrain names to be unique. It remains a create, not an edit-in-place.

Notes for review

  • /api/config gained default_model. The SPA defaulted new versions to models[0], which was claude-sonnet-5 only by luck of the short list. Against a sorted 160-entry catalog that silently becomes an arbitrary Bedrock model. A test pins default_model != models[0] so this cannot regress.
  • pydantic_ai.models is imported inside model_catalog(), not at module scope. It costs ~235ms to import, every CLI invocation loads valcore.settings, and only the API config endpoint needs the catalog. Module-level it took import valcore.settings from 136ms to 373ms.
  • The frozen model field is now readOnly rather than disabled, matching its siblings in the same section. <select> has no readOnly attribute, which is the only reason it differed; as an input a frozen version's model string stays selectable for copying.

Testing

951 Python tests pass (+7), 595 web tests pass (+5), tsc clean.

New coverage: catalog derivation and route completeness; every catalog entry passes validate_model_string; a well-formed name absent from the catalog is accepted while malformed ones are still rejected; default_model is distinct from models[0]; the editor prefills from a seed; the seed is a deep copy so editing the draft cannot mutate the source version; and a blank fallback when an evaluator has no versions yet.

Note this repo has no ESLint config, so no JS linting ran — ruff and pytest (the pre-commit hooks) are clean.

🤖 Generated with Claude Code

Two changes to evaluator version authoring.

Open the model catalog. MODEL_CATALOG was five hand-maintained strings while
the Gateway serves far more, so it is now derived: model_catalog() filters
pydantic-ai's known_model_names() through the existing GATEWAY_ROUTES, yielding
160 models that track whatever version is pinned. Filtering through
GATEWAY_ROUTES rather than a bare "gateway/" prefix means every suggestion is a
string validate_model_string accepts, so the two cannot drift.

The catalog is type-ahead only, not a whitelist. The model field becomes an
input + datalist instead of a select, so any well-formed gateway/<route>:<name>
is enterable and the Gateway stays the authority on what exists.
validate_model_string was already shape-only and is unchanged.

/api/config now also returns default_model. The SPA defaulted new versions to
models[0], which was claude-sonnet-5 by luck of the short list; against a sorted
160-entry catalog it would silently become an arbitrary Bedrock model.

pydantic_ai.models is imported inside model_catalog() rather than at module
scope: it costs ~235ms, every CLI invocation imports valcore.settings, and only
the API config endpoint needs the catalog.

Seed new versions from the one on screen. "New version" opened a blank editor,
losing all the prior text. It now prefills from the version being viewed,
mirroring the server's copy_version, including carrying version_name over
verbatim. It remains a create, not an edit-in-place.

Incidentally, the frozen model field is now readOnly rather than disabled,
matching its siblings; select has no readOnly attribute, which is the only
reason it differed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@duncankmckinnon
duncankmckinnon merged commit 842a6f2 into main Aug 13, 2026
6 checks passed
@duncankmckinnon
duncankmckinnon deleted the model-catalog-and-version-seeding branch August 13, 2026 00:41
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