Skip to content

Commit 48f2e83

Browse files
olddonkeyWibias
andauthored
feat(providers): add bounded model discovery contract (#652)
* feat(providers): add bounded model discovery contract * fix(providers): harden model discovery contract * docs(providers): clarify discovery contains predicates --------- Co-authored-by: Wibias <37517432+Wibias@users.noreply.github.com>
1 parent fff8c36 commit 48f2e83

23 files changed

Lines changed: 1332 additions & 124 deletions

docs-site/src/content/docs/ja/reference/configuration.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,15 @@ token の代わりに使えます。すべての候補は timing side channel
151151
| Field | Type | Meaning |
152152
| --- | --- | --- |
153153
| `adapter` | `string` | `openai-chat``openai-responses``anthropic``google``kiro``cursor``azure-openai`(または別名 `azure`)のいずれか。 |
154-
| `baseUrl` | `string` | 上流 API base URL。 |
154+
| `baseUrl` | `string` | 上流 API base URL。固定 endpoint を持つ大半の組み込み provider は一致しない URL を無視します。新しく追加された衝突保護付き API-key preset は、以前からある同名 custom provider の送信先を維持します。[固定プロバイダーのエンドポイント](#固定プロバイダーのエンドポイント)を参照してください。 |
155155
| `responsesPath?` | `string` | `key` 認証の `openai-responses` リクエストに使う任意の相対 resource path。`/` で始め、URL scheme、query、fragment を含めてはいけません。省略時は従来の `/v1/responses` URL 構築を維持します。 |
156156
| `disabled?` | `boolean` | 設定はディスクに残すがルーティングとモデル/カタログ一覧から除外します。 |
157157
| `apiKey?` | `string` | API キーまたはリクエスト時に解釈する `${ENV_VAR}` / `$ENV_VAR` 参照。 |
158158
| `apiKeyTransport?` | `"x-api-key" \| "bearer"` | Anthropic API キーのヘッダー方式。デフォルトはネイティブの `x-api-key` です。`Authorization: Bearer <key>` が必要な互換 gateway では `"bearer"` を設定します。key 認証の `anthropic` プロバイダーでのみ有効です。 |
159159
| `apiKeyPool?` | `ApiKeyPoolEntry[]` | 複数キーを納める pool。`apiKey` はアクティブ項目を反映します。各項目には `id``key`、選択 `label`、選択数値 `addedAt` があります。 |
160160
| `defaultModel?` | `string` | 明示的なモデルなしでこのプロバイダーを選んだときに使うモデル。 |
161161
| `models?` | `string[]` | seed/fallback モデル一覧。`liveModels``false` ならここにあるモデルだけが発見されます。 |
162-
| `liveModels?` | `boolean` | 起動/同期時にプロバイダーのリアルタイム `/models` カタログを取得します(デフォルト `true`)。`false` なら設定された `models` だけを使います。 |
162+
| `liveModels?` | `boolean` | 起動/同期時にプロバイダーのライブモデルカタログを取得します(デフォルト `true`。組み込み preset は registry の信頼済み URL・クエリ・フィルターを使用でき、カスタム provider は `${baseUrl}/models` がデフォルトです`false` なら設定された `models` だけを使います。 |
163163
| `selectedModels?` | `string[]` | モデル発見後に適用するカタログ allowlist。空でなければその id だけを Codex に公開し、空または省略なら発見したモデルをすべて公開します。 |
164164
| `contextWindow?` | `number` | ルーティングカタログ項目に表示するプロバイダー単位の context-window cap。リアルタイム metadata がより小さければそのままにします。 |
165165
| `modelContextWindows?` | `Record<string,number>` | モデル別 context-window cap。一致するモデルでは `contextWindow` より優先し、より小さいリアルタイム metadata を上げません。 |
@@ -192,6 +192,29 @@ token の代わりに使えます。すべての候補は timing side channel
192192
| `desktopExecutor?` | `DesktopExecutorConfig` | **Cursor 専用。** 外部 computer-use/record-screen コマンド。フィールドは下で説明します。 |
193193
| `unsafeAllowNativeLocalExec?` | `boolean` | **Cursor アダプター専用。** Cursor サーバーが指示したローカル `read` / `write` / `delete` / `ls` / `grep` / `shell` / `fetch` 実行を許可する opt-in escape hatch。デフォルト `false` なのでリモート Cursor メッセージが Codex の承認と sandbox を迂回できません。下記 [Cursor プロバイダー](#cursor-プロバイダー-adapter-cursor) 参照。 |
194194

195+
### 固定プロバイダーのエンドポイント
196+
197+
ルーティングは、adapter がリクエストを受け取る前に provider の endpoint を解決します。大半の
198+
組み込み provider では、config の `baseUrl` より registry の endpoint が優先されます。この段階で
199+
設定 URL を維持するのは次の 4 種類です。
200+
201+
- override を明示的に許可する provider: `ollama``vllm``lm-studio``litellm`
202+
`qwen-cloud``alibaba-token-plan-intl`
203+
- registry endpoint が入力用 template の provider(`azure-openai``cloudflare-ai-gateway` など)。
204+
- 同名衝突を保護する新しい固定 API-key preset。以前からある同名 custom provider が別の送信先を
205+
指している場合、その送信先を維持し、key を新しい registry host へ送りません。
206+
- registry に存在せず、ユーザーが独自に定義した provider。
207+
208+
その後も adapter が解決済み URL を調整する場合があります。たとえば `kiro` adapter は canonical な
209+
`runtime.{region}.kiro.dev` host に対して、import した credential の API region を使います。adapter
210+
ごとの規則は [Adapters](/ja/reference/adapters/) を参照してください。
211+
212+
ルーティングが設定済み `baseUrl` を破棄すると opencodex は警告を出します。registry endpoint は完全に
213+
表示し、設定 URL は origin だけを表示します。path は credential 情報を含む可能性があるため記録しません。
214+
不要な `baseUrl` を削除するか、目的の URL と endpoint が一致する provider を選んでください。地域別
215+
サービスでは正しい項目を使ってください。`alibaba-token-plan` は北京に固定され、
216+
`alibaba-token-plan-intl` は国際 endpoint の override を許可します。
217+
195218
## Cursor プロバイダー(`adapter: "cursor"`
196219

197220
Cursor bridge は実験的です。`ocx login cursor` を実行したのち
@@ -247,6 +270,11 @@ Codex の承認と sandbox ルールを迂回する Cursor ネイティブロー
247270
一部のプロバイダーはリアルタイムモデルカタログが非常に大きいか遅いです。Codex に `models` で固定したモデルだけ
248271
見せるには `liveModels``false` に設定してください。
249272

273+
ライブ discovery は 4 MiB または 2,000 件の生モデル行を超える応答をキャッシュ前に拒否します。
274+
組み込み preset は上限をさらに下げ、混在カタログをチャット対応行だけに絞れます。超過または
275+
不正な応答は stale/static fallback に戻り、不適格な行は除外されます。有効な応答に適格な行が
276+
なければ権威ある空カタログとなり、上限超過の応答が暗黙に切り詰められることはありません。
277+
250278
`liveModels``false``models` が空または省略されると opencodex はそのプロバイダーのルーティング
251279
モデルを 1 つも公開しません。
252280

docs-site/src/content/docs/ko/reference/configuration.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,15 @@ token 대신 쓸 수 있습니다. 모든 후보는 timing side channel을 막
158158
| Field | Type | Meaning |
159159
| --- | --- | --- |
160160
| `adapter` | `string` | `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai`(또는 별칭 `azure`) 중 하나. |
161-
| `baseUrl` | `string` | 업스트림 API base URL. |
161+
| `baseUrl` | `string` | 업스트림 API base URL. 고정 endpoint를 쓰는 대부분의 기본 제공 provider는 일치하지 않는 URL을 무시합니다. 새로 승격된 충돌 보호 API-key preset은 기존의 같은 이름 custom provider 목적지를 유지합니다. [고정 프로바이더 엔드포인트](#고정-프로바이더-엔드포인트)를 참조하세요. |
162162
| `responsesPath?` | `string` | `key` 인증 `openai-responses` 요청에 사용할 선택적 상대 resource path. `/`로 시작해야 하며 URL scheme, query, fragment를 포함할 수 없습니다. 생략하면 기존 `/v1/responses` URL 구성을 유지합니다. |
163163
| `disabled?` | `boolean` | 설정은 디스크에 남기되 라우팅과 모델/카탈로그 목록에서 제외합니다. |
164164
| `apiKey?` | `string` | API 키 또는 요청 시점에 해석할 `${ENV_VAR}` / `$ENV_VAR` 참조. |
165165
| `apiKeyTransport?` | `"x-api-key" \| "bearer"` | Anthropic API 키 헤더 방식입니다. 기본값은 네이티브 `x-api-key`이며, `Authorization: Bearer <key>`를 요구하는 호환 gateway에는 `"bearer"`를 설정합니다. key 인증 `anthropic` 프로바이더에서만 유효합니다. |
166166
| `apiKeyPool?` | `ApiKeyPoolEntry[]` | 여러 키를 담는 pool. `apiKey`는 활성 항목을 반영합니다. 각 항목에는 `id`, `key`, 선택 `label`, 선택 숫자 `addedAt`이 있습니다. |
167167
| `defaultModel?` | `string` | 명시적인 모델 없이 이 프로바이더를 선택했을 때 쓸 모델. |
168168
| `models?` | `string[]` | seed/fallback 모델 목록. `liveModels``false`이면 여기 있는 모델만 발견됩니다. |
169-
| `liveModels?` | `boolean` | 시작/동기화 시 프로바이더의 실시간 `/models` 카탈로그를 가져옵니다(기본 `true`). `false`이면 설정된 `models`만 사용합니다. |
169+
| `liveModels?` | `boolean` | 시작/동기화 시 프로바이더의 실시간 모델 카탈로그를 가져옵니다(기본 `true`). 기본 제공 preset은 registry의 신뢰된 URL·쿼리·필터를 사용할 수 있고, 사용자 지정 provider는 `${baseUrl}/models`가 기본입니다. `false`이면 설정된 `models`만 사용합니다. |
170170
| `selectedModels?` | `string[]` | 모델 발견 뒤 적용할 카탈로그 allowlist. 비어 있지 않으면 해당 id만 Codex에 노출하고, 비어 있거나 생략하면 발견한 모델을 모두 노출합니다. |
171171
| `contextWindow?` | `number` | 라우팅 카탈로그 항목에 표시할 프로바이더 단위 context-window cap. 실시간 metadata가 더 작으면 그대로 둡니다. |
172172
| `modelContextWindows?` | `Record<string,number>` | 모델별 context-window cap. 일치하는 모델에서는 `contextWindow`보다 우선하며 더 작은 실시간 metadata를 올리지 않습니다. |
@@ -202,6 +202,29 @@ token 대신 쓸 수 있습니다. 모든 후보는 timing side channel을 막
202202
| `desktopExecutor?` | `DesktopExecutorConfig` | **Cursor 전용.** 외부 computer-use/record-screen 명령. 필드는 아래에 설명합니다. |
203203
| `unsafeAllowNativeLocalExec?` | `boolean` | **Cursor 어댑터 전용.** Cursor 서버가 지시한 로컬 `read` / `write` / `delete` / `ls` / `grep` / `shell` / `fetch` 실행을 허용하는 opt-in escape hatch. 기본 `false`라 원격 Cursor 메시지가 Codex 승인과 sandbox를 우회하지 못합니다. 아래 [Cursor 프로바이더](#cursor-프로바이더-adapter-cursor) 참조. |
204204

205+
### 고정 프로바이더 엔드포인트
206+
207+
라우팅은 adapter가 요청을 보기 전에 provider endpoint를 결정합니다. 대부분의 기본 제공 provider에서는
208+
config의 `baseUrl`보다 registry endpoint가 우선합니다. 이 단계에서 설정 URL을 유지하는 경우는 네 가지입니다.
209+
210+
- override를 명시적으로 허용하는 provider: `ollama`, `vllm`, `lm-studio`, `litellm`, `qwen-cloud`,
211+
`alibaba-token-plan-intl`.
212+
- registry endpoint가 사용자가 채우는 template인 provider(예: `azure-openai`,
213+
`cloudflare-ai-gateway`).
214+
- 이름 충돌을 보호하는 새 고정 API-key preset. 기존의 같은 이름 custom provider가 다른 목적지를
215+
가리키면 원래 목적지를 유지하며 key를 새 registry host로 보내지 않습니다.
216+
- registry에 없고 사용자가 직접 정의한 provider.
217+
218+
그 뒤에도 adapter가 결정된 URL을 조정할 수 있습니다. 예를 들어 `kiro` adapter는 canonical
219+
`runtime.{region}.kiro.dev` host에서 가져온 credential의 API region을 사용합니다. adapter별 규칙은
220+
[Adapters](/ko/reference/adapters/)를 참조하세요.
221+
222+
라우팅이 설정된 `baseUrl`을 버리면 opencodex가 경고를 출력합니다. registry endpoint는 전체를 표시하고
223+
설정 URL은 origin만 표시합니다. path는 credential 정보를 포함할 수 있으므로 기록하지 않습니다. 사용하지
224+
않는 `baseUrl`을 제거하거나 목적 URL과 endpoint가 일치하는 provider를 선택하세요. 지역별 서비스에서는
225+
올바른 항목을 사용해야 합니다. `alibaba-token-plan`은 베이징으로 고정되고,
226+
`alibaba-token-plan-intl`은 국제 endpoint override를 허용합니다.
227+
205228
## Cursor 프로바이더 (`adapter: "cursor"`)
206229

207230
Cursor bridge는 실험적입니다. `ocx login cursor`를 실행한 뒤
@@ -274,6 +297,11 @@ OpenRouter에서 같은 모델을 제공하는 endpoint마다 prompt cache 지
274297
일부 프로바이더는 실시간 모델 카탈로그가 매우 크거나 느립니다. Codex에 `models`로 고정한 모델만
275298
보이게 하려면 `liveModels``false`로 설정하세요.
276299

300+
실시간 discovery 응답이 4 MiB 또는 원시 모델 행 2,000개를 넘으면 캐시 전에 거부됩니다. 기본 제공
301+
preset은 이 한도를 더 낮추고 혼합 카탈로그를 채팅 가능 행으로 필터링할 수 있습니다. 한도 초과 또는
302+
손상된 응답은 stale/static fallback을 사용하고, 부적격 행은 제외됩니다. 유효한 응답에 적격 행이
303+
없으면 권위 있는 빈 카탈로그가 되며, 한도 초과 응답을 조용히 잘라 사용하지 않습니다.
304+
277305
`liveModels``false`이고 `models`가 비어 있거나 생략되면 opencodex는 해당 프로바이더의 라우팅
278306
모델을 하나도 노출하지 않습니다.
279307

docs-site/src/content/docs/reference/configuration.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,15 +299,15 @@ or bind the forward explicitly to loopback (`ssh -L 127.0.0.1:20100:localhost:10
299299
| Field | Type | Meaning |
300300
| --- | --- | --- |
301301
| `adapter` | `string` | One of `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai` (or alias `azure`). |
302-
| `baseUrl` | `string` | Upstream API base URL. Built-in providers with a fixed endpoint ignore it — see [Fixed provider endpoints](#fixed-provider-endpoints). |
302+
| `baseUrl` | `string` | Upstream API base URL. Most built-in fixed endpoints ignore a mismatch; newly promoted collision-safe key presets preserve an older same-named custom destination. See [Fixed provider endpoints](#fixed-provider-endpoints). |
303303
| `responsesPath?` | `string` | Optional relative resource path for key-auth `openai-responses` requests. It must start with `/` and contain no URL scheme, query, or fragment. When omitted, the adapter keeps its legacy `/v1/responses` URL construction. |
304304
| `disabled?` | `boolean` | Keep the provider on disk but exclude it from routing and model/catalog listings. |
305305
| `apiKey?` | `string` | API key, or an `${ENV_VAR}` / `$ENV_VAR` reference resolved at request time. |
306306
| `apiKeyTransport?` | `"x-api-key" \| "bearer"` | Anthropic API-key header style. Defaults to native `x-api-key`; set `"bearer"` for compatible gateways that require `Authorization: Bearer <key>`. Valid only for key-auth `anthropic` providers. |
307307
| `apiKeyPool?` | `ApiKeyPoolEntry[]` | Multi-key pool. `apiKey` mirrors the active entry; each item has `id`, `key`, optional `label`, and optional numeric `addedAt`. |
308308
| `defaultModel?` | `string` | Model used when this provider is selected without an explicit model. |
309309
| `models?` | `string[]` | Seed/fallback model list. When `liveModels` is `false`, these are the only discovered models. |
310-
| `liveModels?` | `boolean` | Fetch the provider's live `/models` catalog on start/sync (default `true`). Set `false` to use only configured `models`. |
310+
| `liveModels?` | `boolean` | Fetch the provider's live model catalog on start/sync (default `true`). Built-in presets may use a trusted registry URL/query/filter; custom providers default to `${baseUrl}/models`. Set `false` to use only configured `models`. |
311311
| `selectedModels?` | `string[]` | Catalog allowlist applied after discovery. A non-empty list exposes only those ids to Codex; empty/omitted exposes all discovered models. |
312312
| `contextWindow?` | `number` | Provider-wide Codex-visible context-window cap for routed catalog entries. Live metadata below this value is kept. |
313313
| `modelContextWindows?` | `Record<string,number>` | Model-specific context-window caps. These override `contextWindow` for matching model ids and never raise smaller live metadata. |
@@ -351,13 +351,16 @@ or bind the forward explicitly to loopback (`ssh -L 127.0.0.1:20100:localhost:10
351351
### Fixed provider endpoints
352352

353353
Routing resolves a provider's endpoint before any adapter sees it, and for most built-in
354-
providers the registry's own endpoint wins over a `baseUrl` in your config. Three kinds of entry
354+
providers the registry's own endpoint wins over a `baseUrl` in your config. Four kinds of entry
355355
keep the configured URL at this stage:
356356

357357
- providers that opt into an override — `ollama`, `vllm`, `lm-studio`, `litellm`, `qwen-cloud`
358358
and `alibaba-token-plan-intl`;
359359
- providers whose registry endpoint is a template you fill in, such as `azure-openai` and
360360
`cloudflare-ai-gateway`;
361+
- newly promoted fixed API-key presets that protect name collisions: if an older same-named
362+
custom provider points somewhere else, it remains custom instead of sending that key to the new
363+
registry host;
361364
- providers you define yourself, which are not in the registry at all.
362365

363366
Adapters may adjust the resolved URL afterward. The `kiro` adapter, for example, follows the API
@@ -521,6 +524,12 @@ want Codex to see only the models pinned in `models`:
521524
When `liveModels` is `false` and `models` is empty or omitted, opencodex exposes no routed models
522525
for that provider.
523526

527+
Live discovery rejects a response before caching when it exceeds 4 MiB or 2,000 raw model rows.
528+
Built-in presets may lower either limit and filter mixed catalogs to chat-eligible rows. An
529+
oversized or malformed response follows the normal stale/configured fallback path, while
530+
ineligible rows are excluded. A valid result with zero eligible rows remains an authoritative
531+
empty catalog; OpenCodex never silently truncates an over-limit response.
532+
524533
Use `selectedModels` for a different purpose: discovery still runs, but only the selected ids are
525534
published to Codex's catalog and `/v1/models`. The dashboard's full model list remains available so
526535
the allowlist can be changed later.

0 commit comments

Comments
 (0)