Skip to content

feat(backend): add the canonical LLM catalog file and read cache - #13622

Closed
ntindle wants to merge 4 commits into
feat/llm-catalog-schemafrom
feat/llm-catalog-file
Closed

feat(backend): add the canonical LLM catalog file and read cache#13622
ntindle wants to merge 4 commits into
feat/llm-catalog-schemafrom
feat/llm-catalog-file

Conversation

@ntindle

@ntindle ntindle commented Jul 20, 2026

Copy link
Copy Markdown
Member

Why

Part 2 of 5 of the catalog-as-code stack (#13621 → this). Model definitions today are spread across the LlmModel enum, MODEL_METADATA, MODEL_COST/TOKEN_COST, an LD flag, and env vars — five places, no shared schema, nothing validating cross-references. This PR creates the one canonical, schema-validated, PR-reviewed file that centralizes them, updated through git and propagated by CD (catalog-only diffs may ride hotfix→master; the /review bot covers the fast lane).

What

  • catalog.py — the database: 85 models / 8 providers / 17 creators / 4 copilot routing cells, generated from current dev code so file == reality on day one (costs mirror MODEL_COST/TOKEN_COST; routing cells mirror the ChatConfig defaults in canonical slug form; gpt-5.2 recommended per DEFAULT_LLM_MODEL)
  • catalog_model.py — the validated schema: per-model costs, capabilities, visibility (GA/EMPLOYEES/ADMINS/HIDDEN), tier gating, fallback slugs, routing cells
  • registry.pyload_catalog() builds an in-process read cache at startup behind the stable read interface (get_model, get_enabled_models, get_route, get_schema_options, …) that parts 3–4 and Phase B consume. No Redis, no pub/sub, no DB: the file only changes at deploy, so cache coherence is structural
  • Forever-guards + cost-drift tripwires: the catalog must parse, cross-reference cleanly, and match the legacy cost dicts in both directions — centralizing costs now cannot silently diverge from what billing actually charges. The tripwires die in Phase B3 when the dicts do
  • Lifespan wiring is fail-soft: a broken catalog logs and degrades to existing behavior

Nothing consumes the cache yet — dormant by design, same as the schema PR.

Verification

  • 39/39 tests (schema validation edges, forever-guards, all three tripwire directions, loader/read-interface behavior)
  • poetry run format + poetry run lint clean

Checklist

  • Catalog generated from live code, not hand-transcribed
  • Out-of-scope changes: none

Note

Medium Risk
Touches centralized model metadata and credit pricing for all future LLM routing/billing consumers, but startup load is fail-soft and no code paths consume the cache yet; cost tripwires limit silent billing drift.

Overview
Introduces catalog-as-code for LLMs: a new backend.data.llm_registry package with a validated canonical catalog.py (~85 models, providers/creators, per-model credit costs, empty routing matrix for rollout-safe env defaults), catalog_model.py schema, and registry.py that builds an in-process L1 cache (get_model, get_enabled_models, get_route, get_schema_options, etc.) at startup.

rest_api lifespan now calls load_catalog() inside a fail-soft try/except so a bad catalog logs a warning and leaves the registry empty instead of crashing the API.

Adds forever-guard tests (unique slugs, FK refs, routing cell rules) and bidirectional cost-drift tripwires against MODEL_COST / TOKEN_COST so catalog pricing cannot diverge from billing until Phase B3. Nothing reads the cache in production paths yet—dormant until follow-up PRs wire copilot, catalog API, and blocks.

Reviewed by Cursor Bugbot for commit 995cae5. Bugbot is set up for automated code reviews on this repo. Configure here.

Part 2 of 5 of the catalog-as-code stack. backend/data/llm_registry/
catalog.py IS the model database: 85 models / 8 providers / 17 creators /
4 copilot routing cells, generated from the current MODEL_METADATA,
MODEL_COST, TOKEN_COST, and ChatConfig defaults so file == reality on
day one. Updates happen by PR (catalog-only diffs may ride hotfix->
master for CD-speed changes); git history is the audit log.

- catalog_model.py: validated schema — per-model costs (flat credits +
  per-1M token rates), visibility (GA/EMPLOYEES/ADMINS/HIDDEN),
  min_subscription_tier, fallback slugs, routing (surface->mode->tier)
- registry.py: load_catalog() builds the in-process read cache at
  startup; same read interface the copilot resolver (part 3), public
  endpoint (part 4), and Phase B consumers use. No Redis, no pub/sub,
  no DB — the file only changes at deploy, so coherence is free
- forever-guard tests: parse, unique slugs, referential integrity
  (providers/creators/fallbacks/routing cells), cost bounds
- cost-drift tripwires: catalog costs must equal MODEL_COST/TOKEN_COST
  in both directions until Phase B3 flips the reader and deletes the
  dicts — centralizing costs now cannot silently diverge
- lifespan: fail-soft load; empty catalog degrades to pre-catalog
  behavior

Co-authored-by: Bentlybro <Github@bentlybro.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • dev

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1a9e0672-4f1a-463b-a239-19bbb85f4197

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/llm-catalog-file

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.23%. Comparing base (d4de363) to head (995cae5).

Additional details and impacted files
@@                   Coverage Diff                    @@
##           feat/llm-catalog-schema   #13622   +/-   ##
========================================================
  Coverage                    76.23%   76.23%           
========================================================
  Files                         2698     2698           
  Lines                       205494   205494           
  Branches                     19703    19704    +1     
========================================================
+ Hits                        156656   156661    +5     
+ Misses                       44494    44490    -4     
+ Partials                      4344     4343    -1     
Flag Coverage Δ
platform-frontend-e2e 31.09% <ø> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Platform Backend 83.03% <ø> (ø)
Platform Frontend 50.13% <ø> (+0.01%) ⬆️
AutoGPT Libs ∅ <ø> (∅)
Classic AutoGPT 28.43% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ruction

Pre-review-gate findings B2(part)/S1:
- routing cells now carry the vendor-prefixed DOT-form slugs
  (anthropic/claude-sonnet-4.6) — the spelling OpenRouter actually
  serves; bare dashed canonical slugs 404 there. New spelling-convention
  guard test; the reference guard is now slug-tolerant like the router
- catalog construction moved out of import time (get_catalog build-once
  accessor): a bad literal now degrades fail-soft in load_catalog
  callers instead of ImportError-crashing every process — the bad-
  catalog vector IS the fast-edit lane, so this matters

Co-authored-by: Bentlybro <Github@bentlybro.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ntindle
ntindle marked this pull request as ready for review July 20, 2026 22:55
@ntindle
ntindle requested a review from a team as a code owner July 20, 2026 22:55
@ntindle

ntindle commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

/review

@ntindle
ntindle requested review from Bentlybro and kcze and removed request for a team July 20, 2026 22:55

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 61b10ec. Configure here.

Comment thread autogpt_platform/backend/backend/data/llm_registry/registry.py Outdated
Populated default cells (seeded from ChatConfig CODE defaults) would
have silently shadowed the CHAT_*_MODEL env config of any deployment
whose env differs from code defaults — including prod — the moment this
deployed, and would flip models during LD outages (fall-through lands
on the cell, not the env value). Cells now start empty: env stays
authoritative for a (mode, tier) until an operator claims that cell in
a catalog PR. Reference/spelling guards still govern any cells added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ntindle

ntindle commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

Copy link
Copy Markdown

Queued a review for PR #13622 at a877e04.

@autogpt-pr-reviewer

Copy link
Copy Markdown

⚠️ Code review could not be completed

The review failed due to an unexpected error after multiple retries.

If this persists, please contact support with job ID c36eecfa-6ded-4f06-9286-73b20c30fedc.

…with context_window

cursor-bot medium on #13622: None means unknown/no published cap;
substituting context_window published wrong output limits through the
catalog endpoint (a 1M-context model does not emit 1M output tokens).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ntindle

ntindle commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

Copy link
Copy Markdown

Queued a review for PR #13622 at 995cae5.

@autogpt-pr-reviewer autogpt-pr-reviewer 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.

📋 Automated Review — PR #13622

PR #13622 — feat(backend): add the canonical LLM catalog file and read cache
Author: ntindle | Files: 8

🎯 Verdict: REQUEST_CHANGES

PR Description Quality

⚠️ Partial — the description explains the Why (consolidate 5-place model-definition sprawl into one schema-validated file) and What/How well, but it claims "4 copilot routing cells" while the code ships routing={} (zero cells). Update the description to match the intentionally-empty routing matrix before merge (product).

What This PR Does

Introduces a canonical, schema-validated "catalog-as-code" file (catalog.py) describing ~85 LLM models across 8 providers/17 creators, a Pydantic schema (catalog_model.py), and an in-process build-once/read-many cache (registry.py). The cache is wired fail-soft into the FastAPI lifespan so a broken catalog degrades to pre-catalog behavior instead of crashing startup. Nothing consumes the cache yet — it is dormant by design, with consumers deferred to Phase B.

Specialist Findings

🛡️ Security ⚠️ — No SQL, routes, or secrets; attack surface is small. But the read interface (_build_schema_options) filters only on is_enabled and ignores visibility/min_subscription_tier, so a HIDDEN/EMPLOYEES/ADMINS enabled model would be surfaced as GA to any future consumer (registry.py:166).
🟠 Latent authorization/data-exposure gap — must be closed when the first user-facing consumer lands.

🏗️ Architecture ⚠️ — Clean layer separation (schema → data → view), correct fail-soft wiring, and unidirectional coupling. Flags a sort nondeterminism (registry.py:210 vs _build_schema_options), a reverse-integrity gap in the cost tripwire (catalog_test.py:118), triple-duplicated slug-resolver logic, and mutable singleton globals.
🟠 Sort key mismatch and the enabled-slug↔transport integrity gap should be tightened before consumers depend on this surface.

Performance ✅ — Sound build-once/read-many design; O(1) get_model/get_route lookups at trivial scale (n≈85). Several accessors (get_default_model_slug, get_enabled_models, get_all_model_slugs_for_validation) recompute derived views O(n)/O(n log n) per call instead of materializing once at load like _schema_options — microseconds today, cheap to fix before consumers land.

🧪 Testing ⚠️ — Strong bidirectional cost-drift tripwires and correct global-state isolation, but the fail-soft lifespan path (a headline feature) has zero tests (rest_api.py:165), half of RegistryModel's field mapping is unverified (registry_test.py:78), and three routing/fallback "guard" tests are vacuous — they loop over the empty routing={} and cannot fail today.
🟠 Missing negative/degradation coverage on a billing-adjacent path.

📖 Quality ✅ — Grade A-. Excellent docstrings that explain why, well-typed Pydantic models. Minor DRY (_slug_candidates duplicated in catalog_test.py:54) and the same sort-key inconsistency.

📦 Product ⚠️ — Well-guarded dormant infra. Notes the "4 routing cells" description mismatch, the default-vs-dropdown sort divergence, and that supports_* capabilities default all-False (catalog_model.py:88) — confirm these were genuinely absent upstream so a future picker doesn't misreport model capabilities.

📬 Discussion ✅ — 27/27 checks green, no merge conflicts, sole Cursor Bugbot thread resolved + outdated (the "wrong max output fallback" claim is a false positive refuted by code + test_null_max_output_tokens_is_preserved). Note: this is a stacked PR on feat/llm-catalog-schema (#13621 must merge first); CodeRabbit auto-review was skipped due to the non-default base, and there is no human sign-off yet.

🔎 QA ✅ — Ran the code live in the rest_server container: startup log confirms LLM catalog loaded: 85 models, 85 schema options, 0 routing cells; all 39 tests pass on independent re-run; cost-drift tripwires match live MODEL_COST/TOKEN_COST dicts in both directions and trip on injected drift; fail-soft degradation returns None/[] without crashing.

🟠 Should Fix

  1. Fail-soft catalog load is untested (rest_api.py:165) — the headline "degrades on broken catalog" behavior has no test. Monkeypatch load_catalog to raise and assert the exception is swallowed, a warning logs, and get_all_models() returns []. (Flagged by: testing — 1)
  2. Default-model sort disagrees with dropdown sort (registry.py:213) — get_default_model_slug sorts by display_name (case-sensitive) while _build_schema_options sorts by display_name.lower(); the computed default can differ from the visually-first picker entry once >1 model is enabled/recommended. Unify on .lower(). (Flagged by: architect, performance, quality, product — 4 specialists)
  3. Read interface ignores visibility/min_subscription_tier (registry.py:166) — restricted (HIDDEN/EMPLOYEES/ADMINS) enabled models surface as GA, and get_all_model_slugs_for_validation would accept them as user-supplied ids. Filter on visibility (or take a caller-supplied floor) and add a test asserting a restricted model is excluded. (Flagged by: security — 1)
  4. RegistryModel field propagation half-verified (registry_test.py:78) — visibility, min_subscription_tier, fallback_model_slug, kind, and the supports_*/capabilities/extra_metadata fields copied by _build_models are never asserted; a mapping regression would pass silently. Load a model with all non-default fields set and assert each survives the join. (Flagged by: testing — 1)
  5. Vacuous routing/fallback guard tests (catalog_test.py:47, :66, :130) — three guards loop over the empty routing={}/unset fallback_model_slug, so _resolve_cell/tolerant_match are never exercised. Add seeded-payload tests so the resolver logic is proven now, not only when someone edits the catalog. (Flagged by: testing, architect, quality — 3 specialists)
  6. Reverse cost-drift tripwire only checks costed models (catalog_test.py:118) — an enabled slug with a typo and no cost is never cross-checked against a real transport identifier, yet slugs are sent to providers nearly verbatim. Assert every enabled catalog slug resolves to a known LlmModel (or an explicit allow-list). (Flagged by: architect — 1)

🟡 Nice to Have

  1. Precompute read views at load time (registry.py:197, :211, :228) — materialize enabled-models / default-slug / validation-frozenset once in load_catalog() before Phase-B consumers put these on request paths. (performance)
  2. Encapsulate singleton globals in a swappable registry object (registry.py:110) — removes the autouse test-restore fixture and enables clean reload/injection; also makes the multi-global swap atomic. (architect, performance)
  3. Confirm supports_* capability defaults (catalog_model.py:88) — all-False may misreport capable models when a future picker surfaces them. (product)

🔵 Nits

  1. Extract _slug_candidates(value) helper (catalog_test.py:54) — candidate-expansion logic is duplicated verbatim in _resolve_cell and tolerant_match. (architect, quality)
  2. Constrain kind to a Literal (catalog_model.py:76) — free-form str while sibling enumerated dimensions are Literal-gated. (architect)
  3. Fix "4 routing cells" in PR description — code ships routing={}. (product)
  4. Test the 2000-model boundary (catalog_model_test.py:88) — only 2001 (reject) is checked, not exactly MAX_CATALOG_MODELS (accept). (testing)

QA Screenshots

Screenshot Description
frontend healthy after catalog startup load Frontend loads healthy after the new fail-soft startup wiring ✅

Human Review Needed

YES — This centralizes billing-relevant pricing data and adds startup lifespan wiring; CodeRabbit was skipped (non-default base) and there is no human sign-off yet, so a human should eyeball the pricing centralization before merge.

Risk Assessment

Merge risk: LOW | Rollback: EASY — the code is dormant (nothing consumes it), fail-soft, and lives in a self-contained new package; reverting is a clean file removal.

CI Status

❌ 1 check failed — frontend pnpm test:unit failed, but this is a backend-only PR with no frontend source changes, so the failure is unrelated (likely flaky/pre-existing) and should be confirmed against dev. All other checks pass: frontend lint/types/build ✅, backend lint ✅, and per the discussion specialist 27/27 GitHub checks are green. The 39 new backend tests pass on independent re-run.


UI Testing — Variant Results

✅ local: Backend-only dormant catalog package: startup load fires fail-soft, read interface returns 85 models, all 39 tests pass on independent re-run, and cost-drift tripwires match live billing dicts and trip on injected drift.

✅ hosted: Dormant backend catalog PR verified live: catalog loads 85 models at startup, 39/39 tests pass, cost-drift is zero across 137 priced entries, negative/schema validation and fail-soft all behave correctly.


def load_catalog(payload: CatalogPayload | None = None) -> None:
"""Build the L1 structures from the catalog file. Called at startup.

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 (security/authorization / data exposure)

_build_schema_options() (backing get_schema_options) filters only on is_enabled, not on visibility. A model with is_enabled=True and visibility=HIDDEN/EMPLOYEES/ADMINS is returned in the user-facing dropdown options, contradicting the schema's documented contract that HIDDEN serves-but-is-never-shown. Any Phase-B consumer trusting this as user-facing would leak internal/pre-launch models.

Suggestion: Filter on visibility == 'GA' (or accept a caller-supplied visibility floor) in _build_schema_options; add a test asserting a HIDDEN enabled model is excluded from schema options.



def get_route(surface: str, mode: str, tier: str) -> str | None:
"""Return the catalog's routing-cell slug, if the cell is set.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (security/authorization / input validation)

get_all_model_slugs_for_validation() returns every enabled slug regardless of visibility or min_subscription_tier. Its stated purpose is validating user-supplied model ids, so a caller using it as an allow-list would accept EMPLOYEES/ADMINS-only or higher-tier models from a regular user once Phase B wires it up.

Suggestion: When enforcement lands, have this function apply the caller's allowed visibility and subscription tier; add tests covering restricted models being rejected.


class CatalogCreator(BaseModel):
model_config = ConfigDict(frozen=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (security/input validation)

_SLUG_PATTERN permits '/', ':', '.', '-' and slugs/routing cells are forwarded to provider transports nearly verbatim. There is no runtime allow-list on the outbound model string; safety rests entirely on PR review of catalog.py, which now includes a hotfix->master fast lane for catalog-only diffs.

Suggestion: Keep the forever-guard and cost-drift tripwire tests as required status checks on the catalog fast-lane so a single reviewed file cannot silently change what strings are sent to external LLM APIs or what users are billed.

"""Model-selection dropdown options (enabled models only)."""
return list(_schema_options)


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 (architect/determinism/consistency)

get_default_model_slug() sorts models by display_name (case-sensitive), while _build_schema_options() sorts by display_name.lower(). With more than one recommended/enabled model these two orderings can disagree, so the computed default may differ from the picker's first entry.

Suggestion: Use a single shared sort key (e.g. display_name.lower()) in both get_default_model_slug and _build_schema_options.


def test_cost_drift_tripwire_reverse_direction():
"""Every catalog cost corresponds to a real enum model still priced in
the dicts — a model removed from code must not keep a ghost price here."""

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 (architect/data-integrity)

The reverse cost-drift/enum tripwire only validates models where cost is not None. An enabled catalog entry with a bogus or typo'd slug and no cost is never cross-checked against a real LlmModel/transport identifier, yet these slugs are sent to providers nearly verbatim.

Suggestion: Add a guard asserting every enabled catalog slug resolves to a known LlmModel enum member (or an explicit catalog-only allow-list), independent of whether the model carries a cost.

provider: str
context_window: int
max_output_tokens: int | None
display_name: str

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (quality/data model redundancy)

RegistryModelMetadata.display_name (L41) and provider_name (L42) duplicate RegistryModel.display_name (L63) and provider_display_name (L68); both are populated from the same values, giving two ways to read one fact.

Suggestion: Either drop the nested duplicates, or add a one-line comment noting the mirror is required for block-schema compatibility to prevent a future 'fix'.


def get_default_model_slug() -> str | None:
"""First recommended enabled model, else first enabled model."""
models = sorted(_dynamic_models.values(), key=lambda m: m.display_name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (quality/consistency)

get_default_model_slug sorts by m.display_name (case-sensitive) while _build_schema_options sorts by m.display_name.lower(); ordering of the same collection can diverge by capitalization.

Suggestion: Use a single consistent sort key (lowercased) in both functions.

# shadow the CHAT_*_MODEL env config of any deployment whose env
# differs from code defaults, and would flip models during LD
# outages. Cells apply on cloud deployments (behave_as=CLOUD) only.
routing={},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (product/requirements-mismatch)

PR description claims '4 copilot routing cells' but the catalog ships routing={} (zero cells). The behavior is intentional and well-justified in the inline comment, but the description misrepresents what shipped.

Suggestion: Update the PR description to say routing cells ship empty (env stays authoritative), matching the code and the Cursor summary.


def get_default_model_slug() -> str | None:
"""First recommended enabled model, else first enabled model."""
models = sorted(_dynamic_models.values(), key=lambda m: m.display_name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (product/ux-consistency)

get_default_model_slug() sorts by display_name (case-sensitive) while get_schema_options() sorts by display_name.lower(). The default-selected model can differ from the visually first dropdown option once fallback-to-first-enabled kicks in.

Suggestion: Sort both by display_name.lower() so the default model matches the top of the picker list.

# the pre-launch testing state.
visibility: Literal["GA", "EMPLOYEES", "ADMINS", "HIDDEN"] = "GA"
# Null = available on every subscription tier. Enforcement lands with
# the registry-driven picker (Phase B).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 🟢 low (product/data-accuracy)

supports_tools/json_output/reasoning/parallel_tool_calls default to False and no catalog model overrides them, so capable models (e.g. gpt-4o, claude-*) will publish as non-capable when a future picker/catalog endpoint surfaces these flags.

Suggestion: Confirm these capabilities were genuinely absent in the live source data; if not, populate them so the eventual user-facing catalog does not misreport model capabilities.

@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 🚧 Needs work in AutoGPT development kanban Jul 21, 2026
@ntindle

ntindle commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

Consolidated into #13627 — the stack now lands as a single PR that also performs the cutover (the catalog replaces the hand-maintained dicts in the same diff, per review feedback that dormant-then-flip phasing added process without adding safety). All commits from this branch are contained in the consolidated branch.

@ntindle ntindle closed this Jul 21, 2026
@github-project-automation github-project-automation Bot moved this from 🚧 Needs work to ✅ Done in AutoGPT development kanban Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end size/xl

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

1 participant