Resolve model capabilities for custom providers via catalog_provider_id
Problem
For a custom/declarative OpenAI-engine provider, ModelConfig.supports_vision is None and only
resolves from the canonical catalog. #12522 wired with_canonical_vision_support(provider_name)
into apply_canonical_limits, which helps providers whose name matches a canonical id — but, as
noted in #12520, "an arbitrarily-named custom provider never will". So a user with, say,
gpt-5.6-luna served through a personal gateway still gets images stripped.
The reverse case is worse: a provider whose name does resolve (openai, xai, …) works, so
behaviour depends on whether the user happened to name their provider after a catalogued host.
Proposal
Use the already-present catalog_provider_id field on custom provider configs as a fallback
identity for canonical capability resolution. When the configured provider_name isn't in the
catalog, resolve using catalog_provider_id instead.
catalog_provider_id already exists (DeclarativeProviderConfig.catalog_provider_id: Option<String>)
and is consumed today for model-list sourcing and provider matching — but not in the
capability/vision path.
Sketch
Thread the id into resolution, e.g.:
// model_config.rs
fn apply_canonical_limits(provider_name: &str, model: ModelConfig) -> ModelConfig {
if provider_name == goose_providers::azure_foundry::AZURE_FOUNDRY_PROVIDER_NAME {
return model;
}
let canonical_provider = catalog_provider_id_for(provider_name).unwrap_or(provider_name);
model
.with_canonical_limits(canonical_provider)
.with_canonical_vision_support(canonical_provider)
}where catalog_provider_id_for looks up the loaded declarative provider config (already available)
and returns its catalog_provider_id when set. Existing behaviour is preserved when it's unset.
This composes with #12183 (per-model modalities): explicit config should still win, with
catalog_provider_id as the next fallback, then ["text"].
Evidence (measured on v1.52.0 / main @ 98c626d)
- Provider name not in the catalog →
supports_visionNone→ tool-result images replaced by the omission text; the outgoing request has no image part. - Adding exact
<provider>/<model>catalog entries flips it tosupports_vision == Some(true)and the request carries a realimage_urldata part. Confirms the resolution path works and the only gap is how a custom provider tells goose which catalog identity to use.
Alternatives / complements
- #12183 (per-model
modalities) — the most direct fix for arbitrary providers; this issue is complementary for the many custom providers that are simple gateways in front of a catalogued host. - Documenting "name your custom provider after the upstream host" — reject as a footgun.
Caveats
- If
catalog_provider_idmaps to a host whose catalog entries are stale, capabilities could be wrong; explicit config (#12183) should take precedence. - Resolution must remain env-overridable (
GOOSE_*) and must not clamp limits/pricing unintentionally (same caution as thesupports_visionpath).
Source: aaif-goose/goose