Skip to content

Centralize model catalog aliases#183

Merged
AnthonyRonning merged 2 commits intomasterfrom
droid/backend-model-catalog-aliases
Apr 28, 2026
Merged

Centralize model catalog aliases#183
AnthonyRonning merged 2 commits intomasterfrom
droid/backend-model-catalog-aliases

Conversation

@AnthonyRonning
Copy link
Copy Markdown
Contributor

@AnthonyRonning AnthonyRonning commented Apr 27, 2026

Summary

  • Centralize known Tinfoil model metadata in the backend model catalog.
  • Add stable auto:quick and auto:powerful aliases and resolve them at completion/responses boundaries.
  • Expose /v1/models/catalog and sanitize Tinfoil /v1/models while preserving non-Tinfoil provider fallback.

Validation

  • cargo test --manifest-path /Users/tony/Dev/OpenSecret/opensecret/Cargo.toml

Open in Devin Review

Summary by CodeRabbit

  • New Features

    • Added a model catalog endpoint and an OpenAI-style model list; catalog exposes aliases and audio defaults.
  • Bug Fixes

    • Requests now validate and resolve requested models against provider visibility, returning errors for unsupported selections.
  • Changes

    • Default TTS model switched to a newer backend; several models’ context-window values updated and new auto aliases (auto:quick, auto:powerful) available.
  • Tests

    • Expanded tests for alias resolution, catalog filtering, OpenAI list inclusion, and context-window expectations.
  • Chores

    • Append-only updates to release history metadata files.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Walkthrough

Model matching changed from prefix-based heuristics to an explicit per-model registry with metadata and aliases. Alias resolution and canonicalization are used to validate/translate requested completion models (only when api_listed && enabled). New model-list endpoints/builders and request rewrite/billing updates were added.

Changes

Cohort / File(s) Summary
Model Registry & Alias Infrastructure
src/model_config.rs
Replaces prefix matching with an explicit per-model registry (id/provider/display/short/description/access tiers/capabilities/badges, ordering, listed/api_listed/enabled/deprecated flags); adds ModelAliasEntry, public alias constants, resolve_completion_model_id, and JSON builders model_catalog_response() and openai_models_response(); canonicalization now uses exact id matching with limited starts_with fallback.
Completion Request Flow & Handlers
src/web/openai.rs, src/web/responses/handlers.rs
Adds encrypted GET /v1/models/catalog; switches default TTS to qwen3-tts and validates TTS models; integrates resolve_completion_model_id into chat/completion flow (provider-specific resolution/rewrite, billing_context updates, BadRequest for unsupported); refactors /v1/models proxying to use openai_models_response() for tinfoil and fetch_provider_models for others; makes request bodies mutable where needed; updates background title task to use AUTO_QUICK_MODEL_ID.
Token Context Tests
src/tokens.rs
Updates unit test expectations for context-window sizes: removes assertions for several removed/changed models, adds gpt-oss-safeguard-120b, auto:quick (128k), auto:powerful (256k), adjusts fallback/prefix-match expectations.
PCR History Files
pcrDevHistory.json, pcrProdHistory.json
Appends a new history entry to each JSON array with updated PCR digests, timestamp, and signature (data-only additions).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server
    participant Resolver as Model Resolver
    participant Registry as Model Registry
    participant Upstream as Upstream Provider

    Client->>Server: POST /v1/chat/completions { model: "auto:quick", ... }
    Server->>Resolver: resolve_completion_model_id("auto:quick")
    Resolver->>Registry: lookup alias/legacy -> canonical provider id
    Registry-->>Resolver: "llama-3-70b" (api_listed && enabled)
    Resolver-->>Server: "llama-3-70b"

    alt Resolved & allowed
        Server->>Server: set outgoing_body["model"]="llama-3-70b"
        Server->>Server: billing_context.model_name="llama-3-70b"
        Server->>Upstream: POST ... { model:"llama-3-70b", ... }
        Upstream-->>Server: response
        Server-->>Client: encrypted proxied response
    else Not listed/enabled or unsupported
        Server-->>Client: ApiError::BadRequest
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through aliases, tidy and fleet,
Found canonical names and lined up each seat,
Billing kept steady, requests rewritten right,
Catalogs gleam in the soft moonlight,
A rabbit's small cheer for registry complete.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Centralize model catalog aliases' directly and concisely summarizes the main change—introducing a centralized registry of model metadata with alias support—which aligns with the substantial refactoring across model_config.rs, openai.rs, and handlers.rs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch droid/backend-model-catalog-aliases

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

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/web/responses/handlers.rs (1)

358-368: Test may not reflect the actual runtime flow.

This test verifies that build_model_turn_request preserves the model value from body.model. However, in the actual flow through create_response_stream, body.model is already overwritten with the resolved provider ID at line 2504 before build_model_turn_request is called via stream_one_assistant_turn.

The test name implies the alias is preserved for provider resolution, but the integration actually sends the already-resolved provider ID. Consider either:

  1. Renaming the test to clarify it only tests the copy behavior
  2. Adding an integration-level test that verifies the full resolution flow
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web/responses/handlers.rs` around lines 358 - 368, The test
test_build_model_turn_request_preserves_auto_alias_for_provider_resolution is
misleading because at runtime create_response_stream overwrites body.model with
the resolved provider ID (see create_response_stream and
stream_one_assistant_turn) before build_model_turn_request is invoked; either
rename the unit test to something like
test_build_model_turn_request_copies_model_value to reflect it only checks copy
behavior of build_model_turn_request, or add an integration test that exercises
create_response_stream/stream_one_assistant_turn end-to-end: simulate a request
with AUTO_QUICK_MODEL_ID, let the resolver run so body.model is replaced by the
provider ID, call the streaming flow, and assert the outgoing chat request/model
matches the resolved provider ID rather than the alias.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/web/responses/handlers.rs`:
- Around line 358-368: The test
test_build_model_turn_request_preserves_auto_alias_for_provider_resolution is
misleading because at runtime create_response_stream overwrites body.model with
the resolved provider ID (see create_response_stream and
stream_one_assistant_turn) before build_model_turn_request is invoked; either
rename the unit test to something like
test_build_model_turn_request_copies_model_value to reflect it only checks copy
behavior of build_model_turn_request, or add an integration test that exercises
create_response_stream/stream_one_assistant_turn end-to-end: simulate a request
with AUTO_QUICK_MODEL_ID, let the resolver run so body.model is replaced by the
provider ID, call the streaming flow, and assert the outgoing chat request/model
matches the resolved provider ID rather than the alias.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e382398d-160a-4612-bda5-b91f60aca8f9

📥 Commits

Reviewing files that changed from the base of the PR and between bbec100 and e3a5218.

📒 Files selected for processing (2)
  • src/web/openai.rs
  • src/web/responses/handlers.rs

coderabbitai[bot]

This comment was marked as resolved.

AnthonyRonning and others added 2 commits April 27, 2026 20:18
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

Keep model alias resolution provider-aware

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

Update supported model catalog

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@AnthonyRonning AnthonyRonning force-pushed the droid/backend-model-catalog-aliases branch from 56655d3 to 8328d94 Compare April 28, 2026 01:20
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/model_config.rs (1)

510-522: Guard alias exposure with target state.

The catalog always publishes both aliases and defaults, even if a future catalog update disables or hides the target model. In that state model_catalog_response() would still advertise auto:quick / auto:powerful, but resolve_completion_model_id() would return None for them. Filtering aliases/defaults against a target that is still usable would keep the catalog contract consistent.

Suggested direction
     let aliases = MODEL_ALIAS_ENTRIES
         .iter()
+        .filter(|alias| {
+            model_entry(alias.target_model)
+                .is_some_and(|entry| entry.listed && entry.api_listed && entry.enabled)
+        })
         .map(|entry| entry.catalog_json())
         .collect::<Vec<_>>();
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/model_config.rs`:
- Around line 488-495: The current lookup in MODEL_CONFIGS uses a fallback that
matches entries whose id is a prefix of canonical (the .or_else with
canonical.starts_with), which reintroduces the removed suffix-variant heuristic;
change the lookup to only use an exact match by removing the .or_else block so
MODEL_CONFIGS.iter().find(|entry| entry.id == canonical) is the sole lookup,
ensuring suffix variants like "gpt-oss-120b-anything" are not accepted via
prefix matching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1c0deab2-da94-4af9-b989-6d4dc0fdeea2

📥 Commits

Reviewing files that changed from the base of the PR and between 56655d3 and 8328d94.

⛔ Files ignored due to path filters (2)
  • pcrDev.json is excluded by !pcrDev.json
  • pcrProd.json is excluded by !pcrProd.json
📒 Files selected for processing (6)
  • pcrDevHistory.json
  • pcrProdHistory.json
  • src/model_config.rs
  • src/tokens.rs
  • src/web/openai.rs
  • src/web/responses/handlers.rs
✅ Files skipped from review due to trivial changes (2)
  • pcrProdHistory.json
  • pcrDevHistory.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/tokens.rs
  • src/web/responses/handlers.rs
  • src/web/openai.rs

Comment thread src/model_config.rs
Comment on lines 488 to +495
MODEL_CONFIGS
.iter()
.find(|entry| model.starts_with(entry.prefix))
.find(|entry| entry.id == canonical)
.or_else(|| {
MODEL_CONFIGS
.iter()
.find(|entry| canonical.starts_with(entry.id))
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove the residual prefix fallback from model_config.

Lines 491-495 still let any unregistered suffix variant inherit a registered config. For example, gpt-oss-120b-anything will get GPT-OSS limits/sampling here, while resolve_completion_model_id correctly rejects it. That reintroduces the heuristic this PR is trying to remove and can make validation/config/billing disagree.

Suggested fix
     MODEL_CONFIGS
         .iter()
         .find(|entry| entry.id == canonical)
-        .or_else(|| {
-            MODEL_CONFIGS
-                .iter()
-                .find(|entry| canonical.starts_with(entry.id))
-        })
         .map(|entry| entry.config)
         .unwrap_or(DEFAULT_MODEL_CONFIG)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
MODEL_CONFIGS
.iter()
.find(|entry| model.starts_with(entry.prefix))
.find(|entry| entry.id == canonical)
.or_else(|| {
MODEL_CONFIGS
.iter()
.find(|entry| canonical.starts_with(entry.id))
})
MODEL_CONFIGS
.iter()
.find(|entry| entry.id == canonical)
.map(|entry| entry.config)
.unwrap_or(DEFAULT_MODEL_CONFIG)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/model_config.rs` around lines 488 - 495, The current lookup in
MODEL_CONFIGS uses a fallback that matches entries whose id is a prefix of
canonical (the .or_else with canonical.starts_with), which reintroduces the
removed suffix-variant heuristic; change the lookup to only use an exact match
by removing the .or_else block so MODEL_CONFIGS.iter().find(|entry| entry.id ==
canonical) is the sole lookup, ensuring suffix variants like
"gpt-oss-120b-anything" are not accepted via prefix matching.

@AnthonyRonning AnthonyRonning merged commit 15e3193 into master Apr 28, 2026
8 checks passed
@AnthonyRonning AnthonyRonning deleted the droid/backend-model-catalog-aliases branch April 28, 2026 01:27
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