Model Catalog Refresh
Keep src/schema/models.py (the AllModelEnum catalog) current: add models a
provider shipped since the last refresh, remove ones the provider has deprecated
or is actively steering users away from, and make sure src/core/settings.py's
per-provider DEFAULT_MODEL fallbacks point at a model that still exists.
Where model config lives
| What |
File |
| The enum of every supported model, per provider, with the provider's docs URL in each class docstring |
src/schema/models.py (AllModelEnum) |
| Per-provider default model + which models are "available" when a key is set |
src/core/settings.py (Settings.model_post_init) |
| Enum → API model-string mapping, and provider-specific construction quirks (temperature, streaming, tool-binding) |
src/core/llm.py (_MODEL_TABLE, get_model) |
Unit tests asserting get_model builds the right LangChain class per model |
tests/core/test_llm.py |
| Settings/default-model tests |
tests/core/test_settings.py |
| Live smoke test (real API calls) |
scripts/check_live_models.py |
Workflow
- Survey. For each provider block in
src/schema/models.py, fetch the docs
URL already in that class's docstring and diff its current model list against
the enum values present. These docstring URLs are the single canonical
source for where to look — don't hardcode a second copy of them elsewhere,
since provider doc URLs shift over time and duplicated links silently rot.
- If a docstring URL 404s, redirects to a generic landing page, or otherwise
no longer points at a model listing: find the current canonical URL for
that provider's model docs and update the docstring to it as part of this
same change, then continue the survey from the corrected page. Don't skip
the provider just because the old link broke.
- A few providers need a second check beyond the docstring URL: Azure OpenAI
lags OpenAI's own releases (it's deployment-based — check what base models
Azure currently supports), Anthropic has a separate deprecations page worth
checking for retirement dates, and Vertex AI model paths sometimes differ
from the Gemini API's own names for the same model (e.g.
models/gemini-2.5-flash
vs gemini-2.5-flash) — check both columns. Ollama takes a user-supplied
model name at runtime, so there's nothing to add there; just confirm the
generic pass-through in llm.py still works.
docs.aws.amazon.com returns HTTP 403 to WebFetch (it blocks the
fetcher's user agent — this is not an egress-policy denial). Use WebSearch
for Bedrock model IDs instead; it reads the same doc pages server-side and
surfaces the model-card and inference-profile-support pages. The per-model
card pages (.../model-card-anthropic-claude-<model>.html) list the exact
modelId and profile IDs.
- Classify each gap:
- New model, generally available → add it.
- New model, preview/experimental → use judgment; this repo has taken preview
models before (e.g.
gemini-3-pro-preview) when there's no GA alternative yet,
but prefer GA when one exists.
- Existing model deprecated or sunset by the provider → remove it, unless it's
the only model left for that provider (flag that case instead of leaving the
catalog empty).
- Existing model merely superseded but still served → leave it unless the
provider's docs explicitly say to migrate off it.
- Match existing naming conventions when adding a member:
- Enum member names are
SCREAMING_SNAKE_CASE, usually family+size, e.g.
SONNET_45, GEMINI_25_PRO, LLAMA_33_70B. Version numbers drop dots
(4.5 → 45).
- Enum values are the exact string the provider's API expects
(
claude-sonnet-4-5, gemini-2.5-pro, gpt-5.1) — copy verbatim from the
provider docs, don't guess.
- Values must be unique across all the enums in
AllModelEnum —
tests/schema/test_models.py enforces this. Where two providers serve the
same upstream model one side needs a distinguishing form: VertexAIModelName
uses the models/ prefix, so keep it on new Vertex entries.
- Keep provider families grouped and roughly ordered by size/generation within
a class, matching how they read today.
- Apply changes across every coupled location — do not edit only the enum:
src/schema/models.py: add/remove the StrEnum member. Keep the docstring
URL current (see step 1).
src/core/settings.py model_post_init: if the removed model was a
DEFAULT_MODEL fallback, repoint it at a remaining (ideally cheap/fast) model
for that provider — match the intent of the current defaults (e.g.
Haiku/Flash/Nano-tier, not the flagship).
src/core/llm.py: nothing to do for a plain rename/add (the _MODEL_TABLE
and if model_name in ...Name dispatch are enum-driven), but check for any
provider-specific special-casing (see the Groq safeguard-model branch) that
might need to apply to the new model too.
tests/core/test_llm.py, tests/core/test_settings.py: update any test that
hardcodes a model value being removed; add a case for a notable new model if
it has special handling (temperature override, tool-binding quirk, etc).
.env.example, other docs: update only if they name a specific model that
changed (most don't).
- Do not touch provider credentials or add live network calls during the
survey step. This step is pure research + code edit, no API keys needed.
- Live-test what you changed (needs API keys — see "Live testing" below).
- Summarize what was added/removed/repointed and why, citing the provider
doc for each change. Flag any model rename explicitly — it's a breaking change
for existing deployments pinning the old enum value in
DEFAULT_MODEL/
AVAILABLE_MODELS env config, not something to swap silently. Commit and push
per the repo's normal git workflow; open a PR only if asked.
Providers you can't live-verify (Bedrock, Azure, Vertex service-account, DeepSeek, OpenRouter)
Not having a key for a provider is not a reason to skip it — a stale or
broken catalog entry is worse than a doc-sourced one. Update these from docs
just like the rest, and in the PR mark them explicitly as doc-only /
unverified, citing the provider page and calling out any caveat below so the
next person with a key knows exactly what to spot-check. Only leave a provider
untouched when the docs themselves are ambiguous and the change would be a
product decision (e.g. adding a whole new pricing tier), not a freshness update.
- AWS Bedrock — the enum value is passed straight to
ChatBedrock(model_id=...),
so it must be a real Bedrock ID, not a friendly label. Two gotchas:
- The latest Claude models are not invocable on-demand by their base model
ID — a bare
anthropic.claude-... call 400s with "on-demand throughput
isn't supported." They must go through a cross-region inference profile:
the base ID prefixed with a geo (us./eu./apac.) or global.. Prefer
global. (routes dynamically, region-agnostic — the best fit for a catalog
value with no region context) and note in the PR that single-region
deployments not enrolled in Global CRIS should swap the prefix for their geo.
- Bedrock inherits the same sampling-parameter restrictions as the direct
Anthropic API — e.g. a Sonnet-5-class model rejects
temperature. If you
point a Bedrock entry at such a model, mirror the no-temperature branch
that already exists for it in llm.py's Anthropic and Bedrock dispatch.
If AWS credentials happen to be present, boto3.client("bedrock").list_foundation_models()
is the fastest way to confirm real IDs — but note that Bedrock access is a
separate enablement from plain AWS creds, so this can fail with an auth error
even when other AWS calls would work.
- Azure OpenAI is the one genuinely heavier lift, because it's deployment-based
and the catalog is coupled in more places than the enum:
settings.py model_post_init hardcodes a required_models set (currently
{"gpt-4o", "gpt-4o-mini"}) that it validates the AZURE_OPENAI_DEPLOYMENT_MAP
against — bumping the enum means bumping that set and the .env.example
deployment-map sample and the ~7 Azure cases in tests/core/test_settings.py.
llm.py hardcodes temperature=0.5 for the Azure path, but Azure's GPT-5-era
reasoning variants reject temperature (400). If you move Azure onto one,
add a no-temperature branch like the Anthropic/Bedrock Sonnet-5 handling.
- Changing an Azure enum value is a breaking change to every user's
deployment map (they name deployments after these keys). Treat an Azure
generation bump as its own reviewed change, and flag the deployment-map break
loudly — don't fold it silently into a routine refresh.
Live testing
scripts/check_live_models.py sends a trivial one-word prompt to every model of
every provider that has credentials configured in the environment, and reports
PASS/FAIL/SKIP per model. It is deliberately outside the pytest suite (real
network calls, tiny real cost) — run it by hand or from a scheduled trigger with
keys populated:
PYTHONPATH=src uv run python scripts/check_live_models.py # all configured providers
PYTHONPATH=src uv run python scripts/check_live_models.py --provider anthropic google
A SKIP line means no credentials were present for that provider — that's
expected and not a failure. Only treat FAIL rows as build-blocking. Cost is
negligible (a handful of few-token completions per provider) but it's real spend
against real keys — don't wire it into CI or run it on every commit.
If no provider credentials are configured in the current environment, skip this
step entirely rather than failing — the survey/edit step is still fully useful on
its own.
1---2name: model-refresh3description: Periodically audit the LLM model catalog in src/schema/models.py against what each provider currently ships: add newly released models, remove/flag ones the provider has deprecated or discourages, and re-point DEFAULT_MODEL fallbacks at a current model. Use when asked to "check for new models", "update the model list", "refresh the model catalog", or on the scheduled model-refresh trigger.4---56# Model Catalog Refresh78Keep `src/schema/models.py` (the `AllModelEnum` catalog) current: add models a9provider shipped since the last refresh, remove ones the provider has deprecated10or is actively steering users away from, and make sure `src/core/settings.py`'s11per-provider `DEFAULT_MODEL` fallbacks point at a model that still exists.1213## Where model config lives1415| What | File |16|---|---|17| The enum of every supported model, per provider, with the provider's docs URL in each class docstring | `src/schema/models.py` (`AllModelEnum`) |18| Per-provider default model + which models are "available" when a key is set | `src/core/settings.py` (`Settings.model_post_init`) |19| Enum → API model-string mapping, and provider-specific construction quirks (temperature, streaming, tool-binding) | `src/core/llm.py` (`_MODEL_TABLE`, `get_model`) |20| Unit tests asserting `get_model` builds the right LangChain class per model | `tests/core/test_llm.py` |21| Settings/default-model tests | `tests/core/test_settings.py` |22| Live smoke test (real API calls) | `scripts/check_live_models.py` |2324## Workflow25261. **Survey.** For each provider block in `src/schema/models.py`, fetch the docs27 URL already in that class's docstring and diff its current model list against28 the enum values present. These docstring URLs are the **single canonical29 source** for where to look — don't hardcode a second copy of them elsewhere,30 since provider doc URLs shift over time and duplicated links silently rot.31 - **If a docstring URL 404s, redirects to a generic landing page, or otherwise32 no longer points at a model listing:** find the current canonical URL for33 that provider's model docs and update the docstring to it as part of this34 same change, then continue the survey from the corrected page. Don't skip35 the provider just because the old link broke.36 - A few providers need a second check beyond the docstring URL: Azure OpenAI37 lags OpenAI's own releases (it's deployment-based — check what base models38 Azure currently supports), Anthropic has a separate deprecations page worth39 checking for retirement dates, and Vertex AI model paths sometimes differ40 from the Gemini API's own names for the same model (e.g. `models/gemini-2.5-flash`41 vs `gemini-2.5-flash`) — check both columns. Ollama takes a user-supplied42 model name at runtime, so there's nothing to add there; just confirm the43 generic pass-through in `llm.py` still works.44 - **`docs.aws.amazon.com` returns HTTP 403 to `WebFetch`** (it blocks the45 fetcher's user agent — this is not an egress-policy denial). Use `WebSearch`46 for Bedrock model IDs instead; it reads the same doc pages server-side and47 surfaces the model-card and inference-profile-support pages. The per-model48 card pages (`.../model-card-anthropic-claude-<model>.html`) list the exact49 `modelId` and profile IDs.502. **Classify each gap:**51 - *New model, generally available* → add it.52 - *New model, preview/experimental* → use judgment; this repo has taken preview53 models before (e.g. `gemini-3-pro-preview`) when there's no GA alternative yet,54 but prefer GA when one exists.55 - *Existing model deprecated or sunset by the provider* → remove it, unless it's56 the only model left for that provider (flag that case instead of leaving the57 catalog empty).58 - *Existing model merely superseded but still served* → leave it unless the59 provider's docs explicitly say to migrate off it.603. **Match existing naming conventions** when adding a member:61 - Enum member names are `SCREAMING_SNAKE_CASE`, usually family+size, e.g.62 `SONNET_45`, `GEMINI_25_PRO`, `LLAMA_33_70B`. Version numbers drop dots63 (`4.5` → `45`).64 - Enum values are the exact string the provider's API expects65 (`claude-sonnet-4-5`, `gemini-2.5-pro`, `gpt-5.1`) — copy verbatim from the66 provider docs, don't guess.67 - Values must be unique across *all* the enums in `AllModelEnum` —68 `tests/schema/test_models.py` enforces this. Where two providers serve the69 same upstream model one side needs a distinguishing form: `VertexAIModelName`70 uses the `models/` prefix, so keep it on new Vertex entries.71 - Keep provider families grouped and roughly ordered by size/generation within72 a class, matching how they read today.734. **Apply changes across every coupled location** — do not edit only the enum:74 - `src/schema/models.py`: add/remove the `StrEnum` member. Keep the docstring75 URL current (see step 1).76 - `src/core/settings.py` `model_post_init`: if the removed model was a77 `DEFAULT_MODEL` fallback, repoint it at a remaining (ideally cheap/fast) model78 for that provider — match the intent of the current defaults (e.g.79 Haiku/Flash/Nano-tier, not the flagship).80 - `src/core/llm.py`: nothing to do for a plain rename/add (the `_MODEL_TABLE`81 and `if model_name in ...Name` dispatch are enum-driven), but check for any82 provider-specific special-casing (see the Groq safeguard-model branch) that83 might need to apply to the new model too.84 - `tests/core/test_llm.py`, `tests/core/test_settings.py`: update any test that85 hardcodes a model value being removed; add a case for a notable new model if86 it has special handling (temperature override, tool-binding quirk, etc).87 - `.env.example`, other docs: update only if they name a specific model that88 changed (most don't).895. **Do not touch provider credentials or add live network calls during the90 survey step.** This step is pure research + code edit, no API keys needed.916. **Live-test what you changed** (needs API keys — see "Live testing" below).927. **Summarize** what was added/removed/repointed and why, citing the provider93 doc for each change. Flag any model rename explicitly — it's a breaking change94 for existing deployments pinning the old enum value in `DEFAULT_MODEL`/95 `AVAILABLE_MODELS` env config, not something to swap silently. Commit and push96 per the repo's normal git workflow; open a PR only if asked.9798## Providers you can't live-verify (Bedrock, Azure, Vertex service-account, DeepSeek, OpenRouter)99100Not having a key for a provider is **not** a reason to skip it — a stale or101broken catalog entry is worse than a doc-sourced one. Update these from docs102just like the rest, and in the PR mark them explicitly as **doc-only /103unverified**, citing the provider page and calling out any caveat below so the104next person with a key knows exactly what to spot-check. Only leave a provider105untouched when the docs themselves are ambiguous *and* the change would be a106product decision (e.g. adding a whole new pricing tier), not a freshness update.107108- **AWS Bedrock** — the enum *value* is passed straight to `ChatBedrock(model_id=...)`,109 so it must be a real Bedrock ID, not a friendly label. Two gotchas:110 1. The latest Claude models are **not invocable on-demand by their base model111 ID** — a bare `anthropic.claude-...` call 400s with "on-demand throughput112 isn't supported." They must go through a cross-region **inference profile**:113 the base ID prefixed with a geo (`us.`/`eu.`/`apac.`) or `global.`. Prefer114 `global.` (routes dynamically, region-agnostic — the best fit for a catalog115 value with no region context) and note in the PR that single-region116 deployments not enrolled in Global CRIS should swap the prefix for their geo.117 2. Bedrock inherits the same **sampling-parameter restrictions** as the direct118 Anthropic API — e.g. a Sonnet-5-class model rejects `temperature`. If you119 point a Bedrock entry at such a model, mirror the no-`temperature` branch120 that already exists for it in `llm.py`'s Anthropic and Bedrock dispatch.121 If AWS credentials happen to be present, `boto3.client("bedrock").list_foundation_models()`122 is the fastest way to confirm real IDs — but note that Bedrock access is a123 separate enablement from plain AWS creds, so this can fail with an auth error124 even when other AWS calls would work.125- **Azure OpenAI** is the one genuinely heavier lift, because it's deployment-based126 and the catalog is coupled in more places than the enum:127 - `settings.py` `model_post_init` hardcodes a `required_models` set (currently128 `{"gpt-4o", "gpt-4o-mini"}`) that it validates the `AZURE_OPENAI_DEPLOYMENT_MAP`129 against — bumping the enum means bumping that set and the `.env.example`130 deployment-map sample and the ~7 Azure cases in `tests/core/test_settings.py`.131 - `llm.py` hardcodes `temperature=0.5` for the Azure path, but Azure's GPT-5-era132 **reasoning** variants reject `temperature` (400). If you move Azure onto one,133 add a no-`temperature` branch like the Anthropic/Bedrock Sonnet-5 handling.134 - Changing an Azure enum value is a **breaking change** to every user's135 deployment map (they name deployments after these keys). Treat an Azure136 generation bump as its own reviewed change, and flag the deployment-map break137 loudly — don't fold it silently into a routine refresh.138139## Live testing140141`scripts/check_live_models.py` sends a trivial one-word prompt to every model of142every provider that has credentials configured in the environment, and reports143PASS/FAIL/SKIP per model. It is deliberately outside the pytest suite (real144network calls, tiny real cost) — run it by hand or from a scheduled trigger with145keys populated:146147```sh148PYTHONPATH=src uv run python scripts/check_live_models.py # all configured providers149PYTHONPATH=src uv run python scripts/check_live_models.py --provider anthropic google150```151152A `SKIP` line means no credentials were present for that provider — that's153expected and not a failure. Only treat `FAIL` rows as build-blocking. Cost is154negligible (a handful of few-token completions per provider) but it's real spend155against real keys — don't wire it into CI or run it on every commit.156157If no provider credentials are configured in the current environment, skip this158step entirely rather than failing — the survey/edit step is still fully useful on159its own.