Skip to content
Merged
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
38 changes: 38 additions & 0 deletions docs-site/src/content/docs/guides/codex-app-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,44 @@ advertised where it cannot be honored. An `openai-chat` destination can authoriz
eligible model with `chatServiceTier: true`, or authorize only exact models with
`modelSupportsServiceTier`; Responses routes do not need that extra Chat wire authorization.

### Selecting Ultra Fast in Codex Desktop

For a provider that supports `service_tier: "ultrafast"`, declare that wire mapping once.
opencodex then publishes **Ultra Fast** in the routed model's native Codex speed menu;
there is no need to edit `opencodex-catalog.json` after every sync.
For example, merge these fields into an API provider configured for GPT-6.1 Sol:

```json
{
"models": ["gpt-6.1-sol"],
"modelSupportsServiceTier": { "gpt-6.1-sol": true },
"fastWire": {
"kind": "service-tier",
"canonicalToWire": { "priority": "priority", "ultrafast": "ultrafast" },
"foreignCallerTiers": "verbatim"
}
}
```

The mapping applies to every Fast-eligible model on that provider. Use a separate provider
entry if only some of its models support Ultra Fast. Keep opencodex's Fast mode on **Auto**
(omit `fastMode`) so the app's speed selection reaches the provider; forced Fast sends
`priority`, and forced Default drops the tier. Refresh the model catalog, restart Codex
Desktop if it still has the old list, select the routed model, then choose **Ultra Fast**
in its speed menu. Standard remains the default.

Only an eligible `service-tier` wire with an explicit `ultrafast → ultrafast` mapping adds
this choice. Ordinary Fast support, a GPT-like name, native/account rows and combo aliases
do not enable it. Removing the mapping or disabling that model's service-tier support
removes the generated choice from both the catalog and cache on the next successful sync.
The separate `ultraFastTier` setting still only retains hand-supplied catalog tiers.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Correct the ultraFastTier description for native rows

The ultraFastTier setting does not only retain hand-supplied tiers: for bare and account-bound gpt-6-astra and gpt-6.1-sol, mergeCatalogEntriesFromObservedState calls applyNativeUltraFastTier(e, ultraFastTier), which actively adds generated Ultrafast descriptors. This sentence can mislead users about what enabling the setting changes (and the Korean translation repeats the same claim), so qualify it as the routed-row behavior and document the native-row generation here as well.

AGENTS.md reference: docs-site/AGENTS.md:L7-L10

Useful? React with 👍 / 👎.


[OpenAI's API guide](https://developers.openai.com/api/docs/guides/ultrafast-mode) documents
Ultrafast for GPT-6 Astra and GPT-6.1 Sol. API access and
[Codex plan/workspace access](https://learn.chatgpt.com/docs/agent-configuration/speed)
are separate. A menu choice does not grant provider access or guarantee measured speed;
Ultra Fast increases usage/cost, and Codex's own feature requirements still apply.

## Subagent selection

Codex sorts picker-visible catalog entries by ascending `priority` and advertises the first five as
Expand Down
38 changes: 38 additions & 0 deletions docs-site/src/content/docs/ko/guides/codex-app-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,44 @@ fast_mode = true
`service_tier`가 제거됩니다(레지스트리가 정식 OpenAI를 `true`, DeepSeek과 Volcengine Ark를 `false`로 분류). 미분류 커스텀 게이트웨이는 호출자가 준 값을 그대로 보존하고 주입도 받지 않습니다. 따라서
처리 불가능한 곳에 fast 옵션이 노출되지 않으며, 커스텀 게이트웨이는 `true`로 명시적으로 옵트인할 수 있습니다.

### Codex 데스크톱에서 Ultra Fast 선택

`service_tier: "ultrafast"`를 지원하는 프로바이더에는 전송 매핑을 한 번 설정하면 됩니다.
opencodex가 라우팅 모델의 Codex 기본 속도 메뉴에 **Ultra Fast**를 추가하므로, 동기화할 때마다
`opencodex-catalog.json`을 직접 수정할 필요가 없습니다. 예를 들어 GPT-6.1 Sol용 API
프로바이더의 기존 설정에 다음 필드를 병합하세요.

```json
{
"models": ["gpt-6.1-sol"],
"modelSupportsServiceTier": { "gpt-6.1-sol": true },
"fastWire": {
"kind": "service-tier",
"canonicalToWire": { "priority": "priority", "ultrafast": "ultrafast" },
"foreignCallerTiers": "verbatim"
}
}
```

이 매핑은 해당 프로바이더에서 Fast를 사용할 수 있는 모든 모델에 적용됩니다. 일부 모델만
Ultra Fast를 지원한다면 별도 프로바이더 항목을 사용하세요. 앱에서 선택한 속도를 전달하려면
opencodex의 Fast 모드를 **Auto**로 유지하세요(`fastMode` 생략). Fast 강제 모드는 `priority`를
전송하고, Default 강제 모드는 tier를 제거합니다. 모델 카탈로그를 새로고침하고, 앱에 이전
목록이 남아 있다면 Codex 데스크톱을 다시 시작한 뒤 라우팅 모델의 속도 메뉴에서 **Ultra Fast**를
선택하세요. 기본 속도는 Standard로 유지됩니다.

사용 가능한 `service-tier` 전송 경로와 명시적인 `ultrafast → ultrafast` 매핑이 있어야 합니다.
일반 Fast 지원, GPT와 비슷한 이름, 네이티브·계정별 항목, combo 별칭만으로는 추가되지 않습니다.
매핑을 제거하거나 모델의 service-tier 지원을 끄면 다음 동기화 성공 시 카탈로그와 캐시에서
자동 생성된 선택지도 제거됩니다. 별도의 `ultraFastTier` 설정은 기존처럼 사용자가 직접 넣은
카탈로그 tier를 보존하는 용도입니다.

[OpenAI API 문서](https://developers.openai.com/api/docs/guides/ultrafast-mode)는 GPT-6 Astra와
GPT-6.1 Sol의 Ultrafast 지원을 안내합니다. API 사용 권한과
[Codex 요금제·워크스페이스 권한](https://learn.chatgpt.com/docs/agent-configuration/speed)은
별개입니다. 메뉴 표시가 제공자의 사용 권한이나 실측 속도를 보장하지는 않습니다.
Ultra Fast는 사용량·비용이 더 크며 Codex 자체 기능 제한도 계속 적용됩니다.

## 서브에이전트 선택

Codex는 선택기에 보이는 카탈로그 항목을 `priority` 오름차순으로 정렬한 뒤 처음 다섯 개를
Expand Down
2 changes: 1 addition & 1 deletion scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -1830,7 +1830,7 @@
"tray-proxy-deadline.test.ts": "windows",
"tray-proxy.test.ts": "windows",
"types-barrel-identity.test.ts": "config",
"ultrafast-tier-honesty.test.ts": "codex-integration", "native-ultrafast-picker.test.ts": "codex-integration",
"ultrafast-tier-honesty.test.ts": "codex-integration", "configured-ultrafast-picker.test.ts": "codex-integration", "native-ultrafast-picker.test.ts": "codex-integration",
"umans-provider.test.ts": "providers",
"uninstall.test.ts": "cli",
"update-async-routes.test.ts": "server",
Expand Down
7 changes: 5 additions & 2 deletions src/codex/catalog/build-entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { NATIVE_RESERVE_MODEL } from "./native-models";
import { isReserveCatalogProjection, type ReserveCatalogProjection } from "./reserve";
import { deriveEntry, finishUpstreamNativeEntry, isExactComboCatalogEntry } from "./derive-entry";
import { PICKER_ORDER_PRIORITY_BASE, SPAWN_PRIORITY_FIELD } from "./subagent-roster";
import { preserveOperatorUltraFastTiers } from "./operator-tiers";
import { applyConfiguredUltraFast } from "./configured-tiers";
import { applyNativeUltraFastTier } from "./native-ultrafast";

export interface ObservedCatalogEntryBuildInput {
Expand Down Expand Up @@ -474,6 +474,8 @@ export function applyFullModelPickerOrder(entries: RawEntry[], order: readonly s
export interface ObservedCatalogMergeInput {
/** Preserve operator-supplied Ultra Fast only from the exact persisted routed row. */
readonly ultraFastTier?: boolean;
/** Current captured wire declarations; disk rows and templates never populate this set. */
readonly configuredUltraFastSlugs?: ReadonlySet<string>;
readonly catalogModels: readonly RawEntry[];
readonly baselineCatalogModels: readonly RawEntry[];
readonly routedEntries: readonly RawEntry[];
Expand Down Expand Up @@ -515,6 +517,7 @@ export interface ObservedCatalogMergeInput {
*/
export function mergeCatalogEntriesFromObservedState({
ultraFastTier = false,
configuredUltraFastSlugs = new Set(),
catalogModels,
baselineCatalogModels,
routedEntries,
Expand Down Expand Up @@ -866,7 +869,7 @@ export function mergeCatalogEntriesFromObservedState({
if (typeof entry.slug !== "string" || !entry.slug.includes("/")
|| isNativeAliasCatalogEntry(entry) || isExactComboCatalogEntry(entry, exactComboSlugs)
|| trustedAccountBoundNativeCatalogSlug(entry) !== undefined) continue;
preserveOperatorUltraFastTiers(entry, operatorRows.get(entry.slug), ultraFastTier);
applyConfiguredUltraFast(entry, operatorRows.get(entry.slug), configuredUltraFastSlugs.has(entry.slug), ultraFastTier);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Project Ultra Fast into live Codex catalogs

When a Codex client obtains its catalog from GET /v1/models?client_version=..., this overlay never runs: the branch in src/server/index/serve-options.ts builds and returns buildCatalogEntries(...) directly, while applyConfiguredUltraFast is only invoked by mergeCatalogEntriesFromObservedState. The gathered model carries supportsUltraFastTier, but the direct builder only projects supportsServiceTier, so live-discovery rows advertise Fast but not Ultra Fast and those clients cannot select the newly configured tier. Project the metadata in the shared entry derivation, or explicitly cover that HTTP path too.

AGENTS.md reference: src/AGENTS.md:L18-L18

Useful? React with 👍 / 👎.

}
const finalRoutedEntrySet = new Set(finalRoutedEntries);
const degradedPreservedCount = preservedRoutedEntries.filter(entry => {
Expand Down
50 changes: 50 additions & 0 deletions src/codex/catalog/configured-tiers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { COMBO_NAMESPACE } from "../../combos";
import type { ResolvedFastPolicy } from "../../providers/fastwire";
import { catalogModelSlug, type CatalogModel, type RawEntry } from "./parsing";
import { preserveOperatorUltraFastTiers } from "./operator-tiers";

const MANAGED_ULTRAFAST = "opencodex_managed_ultrafast";

/** Advertise Ultra Fast only when the eligible final wire explicitly maps that exact tier. */
export function supportsConfiguredUltraFast(policy: ResolvedFastPolicy | undefined): boolean {
return policy?.eligibility === "eligible"
&& policy.fastWire?.kind === "service-tier"
&& policy.fastWire.canonicalToWire.ultrafast === "ultrafast";
}

/** Carry captured capability to the pure merge without treating persisted metadata as authority. */
export function configuredUltraFastSlugs(models: readonly CatalogModel[]): ReadonlySet<string> {
const declarations = new Map<string, boolean>();
for (const model of models) {
const slug = catalogModelSlug(model);
const supported = model.provider !== COMBO_NAMESPACE && model.nativeAlias !== true
&& model.supportsServiceTier === true && model.supportsUltraFastTier === true;
// A conflicting duplicate cannot lend its capability to the surviving row.
declarations.set(slug, supported && declarations.get(slug) !== false);
}
return new Set([...declarations].filter(([, supported]) => supported).map(([slug]) => slug));
}

/** Apply current configuration, or retain an independently supplied exact-row operator tier. */
export function applyConfiguredUltraFast(
entry: RawEntry,
previous: RawEntry | undefined,
configured: boolean,
retainOperatorTier: boolean,
): void {
// This marker only rejects stale generated metadata; it never authorizes a new tier.
const wasManaged = previous?.[MANAGED_ULTRAFAST] === entry.slug;
preserveOperatorUltraFastTiers(entry, wasManaged || configured ? undefined : previous, retainOperatorTier);
delete entry[MANAGED_ULTRAFAST];
if (!configured) return;
entry.service_tiers = [...(Array.isArray(entry.service_tiers) ? entry.service_tiers : []), {
id: "ultrafast",
name: "Ultra Fast",
description: "Higher speed and usage; provider access required",
}];
entry.additional_speed_tiers = [
...(Array.isArray(entry.additional_speed_tiers) ? entry.additional_speed_tiers : []), "ultrafast",
];
// Advertising a selectable capability does not opt the user into its increased usage.
entry[MANAGED_ULTRAFAST] = entry.slug;
}
4 changes: 4 additions & 0 deletions src/codex/catalog/model-hints.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { canInferVendorContext, vendorContextInference } from "./vendor-context";
import { effectiveProviderAlias, effectiveProviderAliasDecision } from "../../providers/default-aliases";
import { initialModelSelectionPending } from "../../providers/initial-model-selection";
import { supportsConfiguredUltraFast } from "./configured-tiers";
import { execFileSync } from "node:child_process";
import { createHash, createHmac, randomBytes } from "node:crypto";
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
Expand Down Expand Up @@ -251,6 +252,7 @@ export function configuredReasoningSummarySupport(prov: OcxProviderConfig | unde
return modelRecordValue(prov.modelReasoningSummaryDelivery, id) !== undefined ? true : undefined;
}

/** Reapply configured limits and captured wire capabilities without trusting stale discovery hints. */
export function applyProviderConfigHints(
name: string,
prov: OcxProviderConfig,
Expand Down Expand Up @@ -315,6 +317,7 @@ export function applyProviderConfigHints(
contextCap: previousContextCap,
contextCapped: previousContextCapped,
supportsServiceTier: _staleServiceTier,
supportsUltraFastTier: _staleUltraFastTier,
fastTierDescription: _staleFastTierDescription,
providerAlias: _staleProviderAlias,
suppressSyntheticMax: _staleSuppressSyntheticMax,
Expand Down Expand Up @@ -355,6 +358,7 @@ export function applyProviderConfigHints(
...(typeof supportsReasoningSummaries === "boolean" ? { supportsReasoningSummaries } : {}),
...(typeof supportsVerbosity === "boolean" ? { supportsVerbosity } : {}),
...(typeof supportsServiceTier === "boolean" ? { supportsServiceTier } : {}),
supportsUltraFastTier: supportsConfiguredUltraFast(fastPolicy),
...(supportsServiceTier === true && fastPolicy.fastTierDescription !== undefined
? { fastTierDescription: fastPolicy.fastTierDescription }
: {}),
Expand Down
2 changes: 2 additions & 0 deletions src/codex/catalog/parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ export interface CatalogModel {
supportsVerbosity?: boolean;
/** Whether this exact routed model has a verified OpenAI-compatible service tier. */
supportsServiceTier?: boolean;
/** Captured eligible service-tier wire explicitly maps ultrafast to ultrafast. */
supportsUltraFastTier?: boolean;
/** Optional provider-specific copy for the advertised Fast tier. */
fastTierDescription?: string;
supportsReasoningSummaries?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions src/codex/catalog/retained-sync.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { projectAntigravitySelectedModels } from "../../providers/antigravity-effort-families";
import { configuredUltraFastSlugs } from "./configured-tiers";
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { initializeConfigOwnership } from "../../lib/config-ownership";
Expand Down Expand Up @@ -509,6 +510,7 @@ function writeRetainedCatalogSync({
: [];
catalog.models = mergeCatalogEntriesFromObservedState({
ultraFastTier: ultraFastTierEnabled(config),
configuredUltraFastSlugs: configuredUltraFastSlugs(orderedGoModels),
modelPickerOrder,
accountSelectors,
catalogModels: catalogModelsForMerge,
Expand Down
4 changes: 4 additions & 0 deletions src/codex/catalog/routed-gather.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { canInferVendorContext } from "./vendor-context";
import { effectiveProviderAlias, effectiveProviderAliasDecision } from "../../providers/default-aliases";
import { initialModelSelectionPending } from "../../providers/initial-model-selection";
import { supportsConfiguredUltraFast } from "./configured-tiers";
import { execFileSync } from "node:child_process";
import { createHash, createHmac, randomBytes } from "node:crypto";
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
Expand Down Expand Up @@ -319,6 +320,7 @@ function boundCustomNativeReasoning(
return bounded;
}

/** Gather discovered and custom rows under one captured provider policy, including speed capabilities. */
async function gatherRoutedModelsUncached(
config: OcxConfig,
capture: GatherFlightCapture,
Expand Down Expand Up @@ -359,6 +361,7 @@ async function gatherRoutedModelsUncached(
const policy = fastPolicyForModel(apiProvider.provider, model.id, apiProvider.name);
const supported = serviceTierSupportFromPolicy(policy);
if (supported !== undefined) model.supportsServiceTier = supported;
model.supportsUltraFastTier = supportsConfiguredUltraFast(policy);
if (supported === true && policy.fastTierDescription !== undefined) model.fastTierDescription = policy.fastTierDescription;
}
}
Expand Down Expand Up @@ -655,6 +658,7 @@ async function gatherRoutedModelsUncached(
...(Array.isArray(cm.reasoningEfforts) ? { reasoningEfforts: [...cm.reasoningEfforts] } : {}),
...(cm.defaultReasoningEffort ? { defaultReasoningEffort: cm.defaultReasoningEffort } : {}),
...(typeof supportsServiceTier === "boolean" ? { supportsServiceTier } : {}),
supportsUltraFastTier: supportsConfiguredUltraFast(fastPolicy),
...(supportsServiceTier === true && fastPolicy?.fastTierDescription !== undefined
? { fastTierDescription: fastPolicy.fastTierDescription }
: {}),
Expand Down
2 changes: 2 additions & 0 deletions src/codex/convergence.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { projectAntigravitySelectedModels } from "../providers/antigravity-effort-families";
import { configuredUltraFastSlugs } from "./catalog/configured-tiers";
import { join } from "node:path";
import { sameCatalogHealPath } from "./catalog/heal-observation";

Expand Down Expand Up @@ -371,6 +372,7 @@ function prepareCatalog(
);
const mergedModels = mergeCatalogEntriesFromObservedState({
ultraFastTier: ultraFastTierEnabled(config),
configuredUltraFastSlugs: configuredUltraFastSlugs(ordered),
modelPickerOrder,
accountSelectors,
catalogModels,
Expand Down
Loading
Loading