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
11 changes: 10 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -1272,9 +1272,18 @@
"code",
"test"
]
},
{
"login": "evan-choi",
"name": "Evan",
"avatar_url": "https://avatars.githubusercontent.com/u/9690415?v=4",
"profile": "https://github.com/evan-choi",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"linkToUsage": false,
"commitType": "docs"
}
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/e
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kevinsslin"><img src="https://avatars.githubusercontent.com/u/86810837?v=4?s=100" width="100px;" alt="Kevin Lin"/><br /><sub><b>Kevin Lin</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=kevinsslin" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/commits?author=kevinsslin" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Borealin"><img src="https://avatars.githubusercontent.com/u/41241077?v=4?s=100" width="100px;" alt="Borealin"/><br /><sub><b>Borealin</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=Borealin" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/commits?author=Borealin" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BrenticusMaximus"><img src="https://avatars.githubusercontent.com/u/32489248?v=4?s=100" width="100px;" alt="BrenticusMaximus"/><br /><sub><b>BrenticusMaximus</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=BrenticusMaximus" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/commits?author=BrenticusMaximus" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sakthimaran-venom"><img src="https://avatars.githubusercontent.com/u/233523816?v=4?s=100" width="100px;" alt="Sakthimaran"/><br /><sub><b>Sakthimaran</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=sakthimaran-venom" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/commits?author=sakthimaran-venom" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/evan-choi"><img src="https://avatars.githubusercontent.com/u/9690415?v=4?s=100" width="100px;" alt="Evan"/><br /><sub><b>Evan</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=evan-choi" title="Code">💻</a></td>
</tr>
</tbody>
</table>
Expand Down
4 changes: 2 additions & 2 deletions app/modules/api_keys/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ApiKeyCreateRequest(DashboardModel):
default=None, pattern=r"(?i)^(none|minimal|low|medium|high|xhigh|max|ultra)$"
)
allowed_reasoning_efforts: list[str] | None = None
enforced_service_tier: str | None = Field(default=None, pattern=r"(?i)^(auto|default|priority|flex|fast)$")
enforced_service_tier: str | None = Field(default=None, pattern=r"(?i)^(auto|default|priority|flex|(ultra)?fast)$")
traffic_class: str | None = Field(default=None, pattern=r"(?i)^(foreground|opportunistic)$")
transport_policy_override: str | None = None
usage_sections: str | None = None
Expand All @@ -53,7 +53,7 @@ class ApiKeyUpdateRequest(DashboardModel):
default=None, pattern=r"(?i)^(none|minimal|low|medium|high|xhigh|max|ultra)$"
)
allowed_reasoning_efforts: list[str] | None = None
enforced_service_tier: str | None = Field(default=None, pattern=r"(?i)^(auto|default|priority|flex|fast)$")
enforced_service_tier: str | None = Field(default=None, pattern=r"(?i)^(auto|default|priority|flex|(ultra)?fast)$")
traffic_class: str | None = Field(default=None, pattern=r"(?i)^(foreground|opportunistic)$")
transport_policy_override: str | None = None
usage_sections: str | None = None
Expand Down
2 changes: 1 addition & 1 deletion app/modules/api_keys/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1461,7 +1461,7 @@ def _normalize_model_slug(value: str | None) -> str | None:
_REASONING_EFFORT_ORDER = ("minimal", "low", "medium", "high", "xhigh", "max", "ultra")
_SUPPORTED_REASONING_EFFORTS = frozenset({"none", *_REASONING_EFFORT_ORDER})
_SUPPORTED_SELECTABLE_REASONING_EFFORTS = frozenset(_REASONING_EFFORT_ORDER)
_SUPPORTED_SERVICE_TIERS = frozenset({"auto", "default", "priority", "flex"})
_SUPPORTED_SERVICE_TIERS = frozenset({"auto", "default", "priority", "flex", "ultrafast"})


def _normalize_expires_at(value: datetime | None) -> datetime | None:
Expand Down
Binary file added docs/screenshots/api-key-ultrafast-after.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/api-key-ultrafast-before.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ codex-lb refreshes usage on its own schedule and treats upstream samples conserv
**Codex CLI falls back to POST instead of WebSockets.**
Run the [WebSocket verification steps](client-setup.md#verify-websocket-transport). If codex-lb sits behind a reverse proxy, make sure it forwards WebSocket upgrades — see [Remote Access](deployment/remote.md).

## Fast Mode and service tiers
## Fast Mode, Ultrafast, and service tiers

Fast Mode and service-tier behavior is documented in the
Fast Mode, Ultrafast, and service-tier behavior is documented in the
[Responses API compatibility context](https://github.com/Soju06/codex-lb/blob/main/openspec/specs/responses-api-compat/context.md#fast-mode-and-service-tiers).

## Old Codex sessions missing after migrating
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ describe("ApiKeyCreateDialog", () => {
expect(onSubmit.mock.calls[0][0].trafficClass).toBe("opportunistic");
});

it("submits Ultrafast service tier", async () => {
const user = userEvent.setup();
const onSubmit = vi.fn().mockResolvedValue(undefined);

renderWithProviders(
<ApiKeyCreateDialog
open
busy={false}
onOpenChange={vi.fn()}
onSubmit={onSubmit}
/>,
);

await user.type(screen.getByLabelText("Name"), "Ultrafast key");
await user.click(screen.getByRole("combobox", { name: /enforced service tier/i }));
await user.click(await screen.findByRole("option", { name: "Ultrafast" }));
await user.click(screen.getByRole("button", { name: "Create" }));

await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(1);
});

expect(onSubmit.mock.calls[0][0].enforcedServiceTier).toBe("ultrafast");
});

it("renders and submits a transport policy override", async () => {
const user = userEvent.setup();
const onSubmit = vi.fn().mockResolvedValue(undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ function ApiKeyCreateForm({ busy, onClose, onSubmit }: ApiKeyCreateFormProps) {
<SelectItem value="default">{t("common.serviceTier.default")}</SelectItem>
<SelectItem value="priority">{t("common.serviceTier.priority")}</SelectItem>
<SelectItem value="flex">{t("common.serviceTier.flex")}</SelectItem>
<SelectItem value="ultrafast">{t("common.serviceTier.ultrafast")}</SelectItem>
</SelectContent>
</Select>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,20 @@ describe("ApiKeyEditDialog", () => {
const trafficClassSelect = screen.getByRole("combobox", { name: /traffic class/i });
expect(trafficClassSelect).toHaveTextContent("Opportunistic");
});

it("shows the stored Ultrafast service tier", () => {
renderWithProviders(
<ApiKeyEditDialog
open
busy={false}
apiKey={createApiKey({ enforcedServiceTier: "ultrafast" })}
onOpenChange={vi.fn()}
onSubmit={vi.fn()}
/>,
);

expect(screen.getByRole("combobox", { name: /enforced service tier/i })).toHaveTextContent("Ultrafast");
});
});

describe("hasLimitRuleChanges", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,11 @@ function ApiKeyEditForm({ apiKey, busy, onSubmit, onClose }: ApiKeyEditFormProps
</div>

<div className="space-y-1">
<div className="text-sm font-medium">{t("apiKeys.form.enforcedServiceTier")}</div>
<label htmlFor="edit-api-key-enforced-service-tier" className="text-sm font-medium">
{t("apiKeys.form.enforcedServiceTier")}
</label>
<Select value={draft.enforcedServiceTier} onValueChange={(enforcedServiceTier) => updateDraft({ enforcedServiceTier })}>
<SelectTrigger>
<SelectTrigger id="edit-api-key-enforced-service-tier">
<SelectValue placeholder={t("common.options.none")} />
</SelectTrigger>
<SelectContent>
Expand All @@ -331,6 +333,7 @@ function ApiKeyEditForm({ apiKey, busy, onSubmit, onClose }: ApiKeyEditFormProps
<SelectItem value="default">{t("common.serviceTier.default")}</SelectItem>
<SelectItem value="priority">{t("common.serviceTier.priority")}</SelectItem>
<SelectItem value="flex">{t("common.serviceTier.flex")}</SelectItem>
<SelectItem value="ultrafast">{t("common.serviceTier.ultrafast")}</SelectItem>
</SelectContent>
</Select>
</div>
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/features/api-keys/schemas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ describe("ApiKeyCreateRequestSchema", () => {
expect(parsed.enforcedReasoningEffort).toBe("ultra");
});

it("accepts Ultrafast service tier in create payload", () => {
const parsed = ApiKeyCreateRequestSchema.parse({
name: "Ultrafast key",
enforcedServiceTier: "ultrafast",
});

expect(parsed.enforcedServiceTier).toBe("ultrafast");
});

it("accepts a non-empty allowed reasoning effort list", () => {
const parsed = ApiKeyCreateRequestSchema.parse({
name: "Selectable reasoning key",
Expand Down Expand Up @@ -277,6 +286,14 @@ describe("ApiKeyUpdateRequestSchema", () => {

expect(parsed.trafficClass).toBe("opportunistic");
});

it("accepts Ultrafast service tier in update payload", () => {
const parsed = ApiKeyUpdateRequestSchema.parse({
enforcedServiceTier: "ultrafast",
});

expect(parsed.enforcedServiceTier).toBe("ultrafast");
});
});

describe("LimitRuleCreateSchema", () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/api-keys/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const ApiKeyUsageSummarySchema = z.object({
totalCostUsd: z.number().nonnegative().default(0),
});

const SERVICE_TIERS = ["auto", "default", "priority", "flex"] as const;
const SERVICE_TIERS = ["auto", "default", "priority", "flex", "ultrafast"] as const;
export type ServiceTierType = (typeof SERVICE_TIERS)[number];

export const TRAFFIC_CLASSES = ["foreground", "opportunistic"] as const;
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@
"common.serviceTier.default": "Default",
"common.serviceTier.flex": "Flex",
"common.serviceTier.priority": "Priority",
"common.serviceTier.ultrafast": "Ultrafast",
"common.states.active": "Active",
"common.states.disabled": "Disabled",
"common.states.enabled": "Enabled",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@
"common.serviceTier.default": "Default",
"common.serviceTier.flex": "Flex",
"common.serviceTier.priority": "Priority",
"common.serviceTier.ultrafast": "Ultrafast",
"common.states.active": "활성",
"common.states.disabled": "꺼짐",
"common.states.enabled": "켜짐",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@
"common.serviceTier.default": "默认",
"common.serviceTier.flex": "Flex",
"common.serviceTier.priority": "优先",
"common.serviceTier.ultrafast": "Ultrafast",
"common.states.active": "活跃",
"common.states.disabled": "已禁用",
"common.states.enabled": "已启用",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-14
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Context

The Responses request models, upstream transports, request logs, and model registry already carry service tiers as normalized strings. They therefore preserve `ultrafast` without a transport change and can route it using live per-account catalog metadata. The remaining hard-coded allowlists are the API-key CRUD contract and dashboard controls.

OpenAI documents `ultrafast` as an access-controlled processing tier currently available for `gpt-5.6-sol`. Entitlement must therefore come from each account's live upstream catalog instead of a static plan or bootstrap assumption.

## Goals / Non-Goals

**Goals:**

- Make `ultrafast` a supported canonical API-key service tier.
- Expose the tier through the existing dashboard API-key controls.
- Preserve existing entitlement-aware account routing and response-tier logging.
- Add focused regression coverage and user-facing compatibility notes.

**Non-Goals:**

- Invent an `ultrafast` model-name alias.
- Advertise Ultrafast from bootstrap metadata or grant it to a plan statically.
- Add a setting, dependency, or database migration.
- Guess a distinct Ultrafast token price that OpenAI has not published.

## Decisions

1. Add `ultrafast` only to the existing backend and frontend API-key tier allowlists. The request models and transports already pass it through, so adding another normalization layer would duplicate working behavior.
2. Keep `ultrafast` canonical. Unlike the legacy `fast` alias, it is an upstream wire value and must not normalize to `priority`.
3. Reuse live model-catalog routing. An explicit or enforced Ultrafast request can select only accounts whose catalog advertises that tier; the existing enforced-tier fallback still removes it for models that do not advertise it.
4. Do not add Ultrafast to the bundled model catalog. Static metadata cannot prove access to an access-controlled preview and would expose a tier that an imported account may not hold.
5. Keep pricing unchanged. No distinct public Ultrafast token price is available in the official OpenAI documentation, so this change does not introduce a speculative multiplier.

## Risks / Trade-offs

- [An entitled account's catalog does not advertise `ultrafast`] → The existing explicit-tier routing error remains visible instead of silently selecting an ineligible account.
- [OpenAI later publishes distinct Ultrafast pricing] → Add the published rates in a focused pricing change before claiming separate cost accuracy.
- [Dashboard-visible option requires review evidence] → Include before and after screenshots in the PR body as required by the simplicity gates.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

OpenAI introduced an access-controlled Ultrafast processing tier for `gpt-5.6-sol`. codex-lb already preserves unknown request tier strings, but its API-key policy and dashboard reject `ultrafast`, leaving the feature incomplete and untested.

## What Changes

- Accept and persist `ultrafast` as an API-key-enforced service tier.
- Expose Ultrafast in the API key create and edit controls.
- Preserve and forward the canonical `ultrafast` value through Responses-compatible routes.
- Use live upstream model-catalog entitlement data to route Ultrafast requests only to advertising accounts.
- Document the upstream availability constraint and add focused regression coverage.

## Capabilities

### New Capabilities

None.

### Modified Capabilities

- `api-keys`: allow dashboard API keys to enforce the canonical `ultrafast` tier.
- `responses-api-compat`: define pass-through behavior for explicit and enforced Ultrafast requests.
- `model-catalog-compat`: define entitlement-aware account routing for the access-controlled tier.

## Impact

The change affects API-key request validation and normalization, dashboard API-key forms and translations, Responses compatibility documentation, model-catalog routing tests, and focused backend/frontend tests. It adds no dependency, setting, database migration, or bootstrap entitlement metadata.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## ADDED Requirements

### Requirement: API keys can enforce the Ultrafast service tier

The dashboard API key CRUD surface MUST accept and persist `ultrafast` as a canonical enforced service tier. The service MUST return the same canonical value and MUST NOT normalize it to `priority`.

#### Scenario: Create an API key with Ultrafast enforcement

- **WHEN** a dashboard client creates an API key with `enforcedServiceTier: "ultrafast"`
- **THEN** the request is accepted
- **AND** the persisted and returned enforced service tier is `ultrafast`

#### Scenario: Enforce Ultrafast on an advertising model

- **GIVEN** an account model advertises the `ultrafast` service tier
- **WHEN** a request uses an API key whose enforced service tier is `ultrafast`
- **THEN** the upstream request carries `service_tier: "ultrafast"`
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## ADDED Requirements

### Requirement: Ultrafast routing follows live account entitlement

The system MUST treat `ultrafast` as an access-controlled service tier and MUST derive account eligibility from live or retained per-account upstream catalog metadata. The bundled bootstrap catalog MUST NOT invent Ultrafast entitlement.

#### Scenario: Only an advertising account is eligible

- **GIVEN** two accounts advertise `gpt-5.6-sol`
- **AND** only one account advertises the `ultrafast` service tier
- **WHEN** a request explicitly asks for `service_tier: "ultrafast"`
- **THEN** account selection considers only the advertising account

#### Scenario: Bootstrap metadata does not grant preview access

- **WHEN** no live or retained account catalog advertises `ultrafast`
- **THEN** bootstrap model metadata does not expose or grant that tier
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## ADDED Requirements

### Requirement: Responses routes preserve the Ultrafast service tier

Responses-compatible routes MUST accept the canonical `ultrafast` service tier and MUST forward it unchanged. When upstream reports the actual response tier, request logging MUST preserve `ultrafast` using the existing requested, actual, and billable tier contract.

#### Scenario: Explicit Ultrafast request is forwarded

- **WHEN** a client sends a Responses request with `service_tier: "ultrafast"`
- **THEN** the forwarded upstream payload contains `service_tier: "ultrafast"`

#### Scenario: Upstream confirms Ultrafast processing

- **WHEN** upstream completes a request with `response.service_tier: "ultrafast"`
- **THEN** the actual and billable request-log tiers are `ultrafast`
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## 1. Backend support

- [x] 1.1 Accept and persist canonical `ultrafast` API-key enforcement values
- [x] 1.2 Add focused request, API-key, catalog-routing, and logging regression coverage

## 2. Dashboard and documentation

- [x] 2.1 Add Ultrafast to dashboard schemas, create/edit controls, and translations
- [x] 2.2 Add frontend schema and interaction coverage for the new option
- [x] 2.3 Document official availability, entitlement behavior, and a concrete request example

## 3. Verification

- [x] 3.1 Validate OpenSpec artifacts and run focused backend/frontend checks
- [x] 3.2 Run the repository local CI gate and capture dashboard before/after evidence
16 changes: 16 additions & 0 deletions openspec/specs/api-keys/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1403,3 +1403,19 @@ reclamation does.
- **WHEN** stale usage-reservation reclamation runs
- **THEN** the reservation stays `reserved`

### Requirement: API keys can enforce the Ultrafast service tier

The dashboard API key CRUD surface MUST accept and persist `ultrafast` as a canonical enforced service tier. The service MUST return the same canonical value and MUST NOT normalize it to `priority`.

#### Scenario: Create an API key with Ultrafast enforcement

- **WHEN** a dashboard client creates an API key with `enforcedServiceTier: "ultrafast"`
- **THEN** the request is accepted
- **AND** the persisted and returned enforced service tier is `ultrafast`

#### Scenario: Enforce Ultrafast on an advertising model

- **GIVEN** an account model advertises the `ultrafast` service tier
- **WHEN** a request uses an API key whose enforced service tier is `ultrafast`
- **THEN** the upstream request carries `service_tier: "ultrafast"`

Loading
Loading