You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Separate provider facts, model facts, provider model offerings, route resolution, and UI projections so CodeWhale stops mixing provider identity, model identity, and provider-specific wire ids.
The key invariant:
A model string alone is never enough to select a route.
Execution requires a ReadyRouteCandidate.
A ReadyRouteCandidate can only be produced by RouteResolver.
RouteResolver resolves provider-scoped model references against catalog facts, user config,
auth config, endpoint config, capability requirements, and validation.
Core Problem
Current provider/model logic mixes several different meanings into one string:
canonical model identity, such as a provider-agnostic logical model;
provider identity, such as DeepSeek, OpenRouter, Together, Ollama, or a custom endpoint;
provider wire model id, such as deepseek-ai/DeepSeek-V4-Pro on Together or deepseek/deepseek-v4-pro on OpenRouter;
aggregator namespace hints, such as anthropic/..., openai/..., deepseek-ai/..., or qwen/...;
user custom model ids for local or OpenAI-compatible endpoints.
Those are not interchangeable. A prefixed wire id can carry provider/catalog namespace information, but it is not enough to decide canonical ownership or route validity outside the provider offering that supplied it.
Examples:
deepseek-ai/DeepSeek-V4-Pro can be a Together wire id.
deepseek/deepseek-v4-pro can be an OpenRouter wire id.
A custom OpenAI-compatible endpoint might legitimately use a string that resembles an aggregator namespace.
So CodeWhale must stop treating deepseek-ai/ or deepseek/ as proof that the active provider must be DeepSeek, or that a non-DeepSeek provider is invalid.
Architecture Contract
Use three catalog concepts plus one runtime concept:
ProviderDescriptor: provider identity and transport facts only.
ModelProfile: canonical, provider-agnostic model facts.
ProviderModelOffering: provider + canonical model + provider-owned wire id + endpoint + provider-specific overrides.
ReadyRouteCandidate: runtime-resolved executable route produced by the route resolver.
Provider facts describe how to talk to a provider.
Model facts describe what a model can do.
Provider model offerings describe which provider serves which model under which wire id.
Route resolution is the only layer that combines provider facts, model facts, config, auth, base URL, aliases, and validation into an executable route.
UI consumes projections of candidates, never raw string matches.
Data Ownership
Provider-owned:
provider id/display/config key/aliases;
env vars/auth schemes;
default base URL/default endpoint;
wire format/request protocol;
live model support;
model-id policy.
Model-owned:
canonical model id;
display name/family/aliases;
intrinsic context window/max output;
modalities;
reasoning/tool/json/streaming traits;
supported params and tool budget hints;
provenance.
Offering-owned:
provider + canonical model relation;
provider wire model id;
provider-scoped aliases;
endpoint key;
provider-specific capability overrides;
route/default hints;
provider/offering-scoped pricing SKU and/or usage meter metadata.
Runtime-derived:
resolved base URL;
auth source/key readiness;
pass-through/custom mode;
normalized reasoning effort;
merged resolved-route capability profile;
resolved-route pricing/usage display;
health/live-cache freshness;
final config snapshot.
Wire Id And Namespace Rule
Provider-prefixed or organization-prefixed model strings are wire ids or namespace hints unless a provider-scoped offering says otherwise.
Do:
Resolve provider + model selector inside that provider's catalog/offering scope.
Keep wire_model_id separate from canonical_model_id.
Preserve custom/pass-through model strings exactly for local/custom endpoints.
Let aggregators and broad catalogs be permissive for syntactically valid unknown ids.
Reject direct-provider contamination only when CodeWhale is confident the active provider cannot serve that route.
Do not:
Infer provider switching from deepseek-ai/, deepseek/, anthropic/, openai/, or any similar prefix.
Treat a provider namespace prefix as canonical model ownership by itself.
Reject hosted aggregator offerings because the wire id appears to belong to another organization.
Add more model.contains(...) validation paths.
Usage And Cost Rule
Do not assume every route has token SKU pricing.
Resolved-route usage/cost metadata should support:
token pricing with provenance, such as DeepSeek native or many hosted API routes;
subscription/quota usage percentage and reset metadata, such as ChatGPT/Codex OAuth-style routes when available;
account credits when a provider exposes balance/credit data;
local/resource/not-applicable states for local runtimes;
explicit unknown/stale states for custom or unsupported providers.
UI should show the route-appropriate meter. Do not show fake token pricing for OAuth/subscription routes, and do not imply local or unknown routes are free.
Storage Split
Static bundled data should contain provider descriptors, built-in model seeds, known route mappings/offerings, conservative model profiles, alias/deprecation rules, sourced pricing seeds, and known usage-meter capabilities.
Live cached provider data should contain secret-free /models results, provider model ids, provider-returned context/pricing/parameter hints when available, usage/quota hints when available, reachability/health, fetched_at, ttl, and provenance.
User config should contain only user intent and overrides: selected provider/model, custom base URL, credentials/auth mode, fallback chain, custom model ids, explicit profile/pricing/usage overrides, routing preferences, Fleet model class preferences, and cost-saving mode.
Goal
Separate provider facts, model facts, provider model offerings, route resolution, and UI projections so CodeWhale stops mixing provider identity, model identity, and provider-specific wire ids.
The key invariant:
Core Problem
Current provider/model logic mixes several different meanings into one string:
deepseek-ai/DeepSeek-V4-Proon Together ordeepseek/deepseek-v4-proon OpenRouter;anthropic/...,openai/...,deepseek-ai/..., orqwen/...;Those are not interchangeable. A prefixed wire id can carry provider/catalog namespace information, but it is not enough to decide canonical ownership or route validity outside the provider offering that supplied it.
Examples:
deepseek-ai/DeepSeek-V4-Procan be a Together wire id.deepseek/deepseek-v4-procan be an OpenRouter wire id.So CodeWhale must stop treating
deepseek-ai/ordeepseek/as proof that the active provider must be DeepSeek, or that a non-DeepSeek provider is invalid.Architecture Contract
Use three catalog concepts plus one runtime concept:
ProviderDescriptor: provider identity and transport facts only.ModelProfile: canonical, provider-agnostic model facts.ProviderModelOffering: provider + canonical model + provider-owned wire id + endpoint + provider-specific overrides.ReadyRouteCandidate: runtime-resolved executable route produced by the route resolver.Provider facts describe how to talk to a provider.
Model facts describe what a model can do.
Provider model offerings describe which provider serves which model under which wire id.
Route resolution is the only layer that combines provider facts, model facts, config, auth, base URL, aliases, and validation into an executable route.
UI consumes projections of candidates, never raw string matches.
Data Ownership
Provider-owned:
Model-owned:
Offering-owned:
Runtime-derived:
Wire Id And Namespace Rule
Provider-prefixed or organization-prefixed model strings are wire ids or namespace hints unless a provider-scoped offering says otherwise.
Do:
provider + model selectorinside that provider's catalog/offering scope.wire_model_idseparate fromcanonical_model_id.Do not:
deepseek-ai/,deepseek/,anthropic/,openai/, or any similar prefix.model.contains(...)validation paths.Usage And Cost Rule
Do not assume every route has token SKU pricing.
Resolved-route usage/cost metadata should support:
UI should show the route-appropriate meter. Do not show fake token pricing for OAuth/subscription routes, and do not imply local or unknown routes are free.
Storage Split
Static bundled data should contain provider descriptors, built-in model seeds, known route mappings/offerings, conservative model profiles, alias/deprecation rules, sourced pricing seeds, and known usage-meter capabilities.
Live cached provider data should contain secret-free
/modelsresults, provider model ids, provider-returned context/pricing/parameter hints when available, usage/quota hints when available, reachability/health, fetched_at, ttl, and provenance.User config should contain only user intent and overrides: selected provider/model, custom base URL, credentials/auth mode, fallback chain, custom model ids, explicit profile/pricing/usage overrides, routing preferences, Fleet model class preferences, and cost-saving mode.
Module Direction
Suggested eventual boundary:
Keep dependency direction clear:
Rules:
Relationship To Child Issues
ReadyRouteCandidatemutation gate./providerreadiness dashboard projection.ModelProfiledescriptors and capability metadata.Acceptance Criteria
Out Of Scope