fix(api): reject doubled provider prefixes in agent model ids (#334)#382
Conversation
Reviewer's GuideAdds config-layer validation to reject agent model identifiers whose model-name segment starts with a second known provider prefix, and ensures invalid persisted model-id overrides are skipped on startup, with comprehensive tests across validator, schema, settings, route, and service layers, plus documentation via a PRP file. Sequence diagram for apply_overrides_on_startup re-validating model-id overridessequenceDiagram
participant ConfigService
participant Validator
participant Settings
participant Logger
ConfigService->>ConfigService: apply_overrides_on_startup(db)
ConfigService->>ConfigService: iterate overrides.items()
alt key in _MODEL_ID_KEYS and isinstance(value, str)
ConfigService->>Validator: validate_model_identifier(value)
alt validate_model_identifier raises ValueError
ConfigService->>Logger: warning(config.override_invalid_model_id)
ConfigService->>ConfigService: continue loop (skip override)
else validate_model_identifier returns
ConfigService->>Settings: setattr(settings, key, value)
end
else other override keys
ConfigService->>Settings: setattr(settings, key, value)
end
Flow diagram for validate_model_identifier nested provider prefix rejectionflowchart TD
Start([Input model identifier v])
CheckColon{"v contains ':'?"}
Start --> CheckColon
CheckColon -- No --> ErrNoColon["raise ValueError: expected 'provider:model-name'"]
CheckColon -- Yes --> Split["provider, model_name = v.split(':', 1)"]
BlankModel{"model_name blank?"}
Split --> BlankModel
BlankModel -- Yes --> ErrBlank["raise ValueError: blank model name"]
BlankModel -- No --> ProviderAllowed{"provider in VALID_MODEL_PROVIDERS?"}
ProviderAllowed -- No --> ErrUnknown["raise ValueError: Unknown provider"]
ProviderAllowed -- Yes --> NestedColon{": in model_name?"}
NestedColon -- No --> Ok["return v (valid identifier)"]
NestedColon -- Yes --> GetNested["nested = model_name.split(':', 1)[0]"]
IsProviderNested{"nested in VALID_MODEL_PROVIDERS?"}
GetNested --> IsProviderNested
IsProviderNested -- No --> Ok
IsProviderNested -- Yes --> Suggest["suggested = provider + ':' + model_name.split(':', 1)[1]"]
Suggest --> ErrNested["raise ValueError: Nested provider prefix + 'Did you mean ' + suggested"]
Ok --> End([Return unchanged v])
ErrNoColon --> End
ErrBlank --> End
ErrUnknown --> End
ErrNested --> End
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Summary
Closes #334 — Reliability E1 (Foundation epic of umbrella #380). Implements
PRPs/PRP-reliability-E1-doubled-provider-prefixes.md.A model identifier whose model-name part starts with another known provider prefix (e.g.
google-gla:google-gla:gemini-3-flash-preview) previously passed validation, persisted viaPATCH /config/ai, and only failed far downstream as a Gemini 404 at agent-run time. pydantic-ai 1.96.0parse_model_idpasses everything after the first colon verbatim, so the validation burden is entirely on this repo's config layer.Changes
app/core/config.py—validate_model_identifiernow rejects a nested provider prefix inside the model name with a "Did you mean<corrected>?" message. Ollamaname:tagidentifiers (ollama:llama3.1:8b,ollama:gemma4:e2b) stay valid — the rule fires only when the first segment of the model name is itself a known provider. Unknown-provider check still outranks shape (pinecone:pinecone:x→ "Unknown provider").app/features/config/service.py—apply_overrides_on_startupre-validates the two model-id override keys; a malformed persisted row is skipped withconfig.override_invalid_model_id(env/default stays in effect; the no-crash-on-startup contract stands).ollama:openaiedge), strict-modemodel_validateJSON-path cases, Settings-boot rejection + acceptance, route-level 422 problem+json assertingcode/errors[0].field/message, and a startup skip-and-warn service test with settings-singleton restore.Validation
ruff check+ruff format --checkclean;mypy --strict+pyright --strictclean (0 errors)pytest -m "not integration": 1888 passed (pre-existingollama:qwen3:8bconsumer fixtures untouched and green)"Nested provider prefix … Did you mean 'google-gla:gemini-3-flash-preview'?";anthropic:claude-sonnet-4-5→ 200;ollama:llama3.1:8b→ 200Out of scope (per PRP)
Frontend input sanitization in
ai-models-panel.tsx; #335 fallback-failure surfacing (E2); normalization mode; backfill of existing malformedapp_configrows.Summary by Sourcery
Reject invalid agent model identifiers with nested provider prefixes and guard persisted overrides from applying malformed IDs at startup.
Bug Fixes:
Enhancements:
Tests: