Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Aliases are optional short request names. They never change the native model id
}
```

Aliases match case-insensitively. A model alias works as `or/opus` or, when globally unique, bare `opus`; an ambiguous bare alias reports its qualified candidates. A provider's `defaultAliases` value overrides `defaultModelAliases`. Built-ins are skipped when multiple models in one provider match the same pattern.
Aliases match case-insensitively. A model alias works as `or/opus` or, when globally unique, bare `opus`; an ambiguous bare alias reports its qualified candidates. Codex model pickers show the qualified alias while preserving the canonical `provider/model` routing id. A provider's `defaultAliases` value overrides `defaultModelAliases`. Built-ins are skipped when multiple models in one provider match the same pattern.

Valid values in `config.json` override built-in defaults. Missing optional fields use the defaults
documented on the domain pages. `OPENCODEX_HOME` takes precedence over the default configuration
Expand Down
12 changes: 11 additions & 1 deletion src/codex/catalog/provider-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryT
import { parseAntigravityAvailableModels, registerAntigravityDiscoveredWireModels } from "../../providers/antigravity-models";
import { applyProviderContextCap, providerContextCap, resolveUnknownRoutedContextWindow } from "../../providers/context-cap";
import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
import { effectiveModelAliases } from "../../providers/default-aliases";
import { routedSlug, slugEquals, slugEquivalenceKey, slugsEquivalent } from "../../providers/slug-codec";
import { CODEX_GPT5_IDENTITY_LINE } from "../../adapters/identity";
import { filterCursorConfiguredModelsByLiveDiscovery } from "../../adapters/cursor/discovery";
Expand Down Expand Up @@ -2147,6 +2148,12 @@ async function gatherRoutedModelsUncached(
// Custom rows override discovered rows that encode to the same Codex-facing slug.
const customKeys = new Set(customModels.map(c => routedSlug(c.provider, c.id)));
const deduped = all.filter(m => !customKeys.has(routedSlug(m.provider, m.id)));
const models = [...deduped, ...customModels];
// ponytail: catalog-scale scan; index ids by provider if catalog growth makes this measurable.
const aliasDisplayNames = new Map(activeProviders.flatMap(({ name, provider }) => (
[...effectiveModelAliases(config, provider, models.filter(model => model.provider === name).map(model => model.id))]
.map(([id, { alias }]) => [`${name}/${id}`, `${provider.alias || name}/${alias}`] as const)
)));
const providerModelOutcomes = providerResults.map(result => (
result.outcome.provider === OPENAI_API_PROVIDER_ID
&& capture.openAiApiPolicy.state === "captured"
Expand All @@ -2155,7 +2162,10 @@ async function gatherRoutedModelsUncached(
: result.outcome
));
return {
models: [...deduped, ...customModels],
models: models.map(model => {
const displayName = aliasDisplayNames.get(`${model.provider}/${model.id}`);
return displayName && !model.displayName ? { ...model, displayName } : model;
}),
comboOmissions: localOmissions,
providerAuthOutcomes: localProviderAuthOutcomes,
providerModelOutcomes,
Expand Down
5 changes: 3 additions & 2 deletions structure/03_catalog-and-subagents.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ liveness contract.
## Entry shape

Routed entries keep Codex-required metadata such as reasoning levels, shell type, API support flags,
base instructions, modalities, auto-compact fields, and strict parser booleans. The public slug and
display name use `provider/model`.
base instructions, modalities, auto-compact fields, and strict parser booleans. The public slug uses
the canonical `provider/model`; its display name uses the qualified provider/model alias when
configured, without changing the routing slug.

## Native passthrough

Expand Down
27 changes: 27 additions & 0 deletions tests/codex-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1921,6 +1921,33 @@ describe("configured CatalogModel displayName -> catalog display_name", () => {
}
});

test("provider and model aliases label picker rows without changing routing slugs", async () => {
clearModelCache("google-antigravity");
try {
const models = await gatherRoutedModels({
port: 10100,
defaultProvider: "google-antigravity",
providers: {
"google-antigravity": {
baseUrl: "https://example.invalid/v1",
adapter: "openai-chat",
liveModels: false,
models: ["gemini-3.7-flash"],
alias: "ga",
modelAliases: { "gemini-3.7-flash": "g3f" },
},
},
});
const row = buildCatalogEntries(nativeTemplate(), [], models)
.find(entry => entry.slug === "google-antigravity/gemini-3.7-flash");

expect(row?.display_name).toBe("ga/g3f");
expect(row?.slug).toBe("google-antigravity/gemini-3.7-flash");
} finally {
clearModelCache("google-antigravity");
}
});

test("a custom row clamps its soft budget to the provider max-input ceiling", async () => {
const models = await gatherRoutedModels({
port: 10100,
Expand Down
Loading