diff --git a/docs-site/src/content/docs/getting-started/quickstart.md b/docs-site/src/content/docs/getting-started/quickstart.md index ed8d79858..1e8d9ec84 100644 --- a/docs-site/src/content/docs/getting-started/quickstart.md +++ b/docs-site/src/content/docs/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init` walks you through: -1. **Pick a provider** — choose one of the 50 built-in registry presets or `custom` to type a base +1. **Pick a provider** — choose one of the 61 built-in registry presets or `custom` to type a base URL and adapter. 2. **API key** — paste a key, or reference an environment variable like `${ANTHROPIC_API_KEY}`. 3. **Default model** — for key, local, and custom providers, accept the preset or enter a model id. diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index 66225e266..0e8cc260f 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -190,7 +190,7 @@ selectors, then retry. Signing in from a machine with no existing `kiro-cli` ses ## 3. API-key catalog -opencodex ships 53 built-in presets: 42 key-based, seven OAuth, three local, and the default +opencodex ships 61 built-in presets: 50 key-based, seven OAuth, three local, and the default ChatGPT-forward preset. The dashboard's **Add provider** picker opens a key provider's dashboard, validates the key, and stores it. Notable entries: @@ -207,6 +207,7 @@ validates the key, and stores it. Notable entries: | MiniMax · MiniMax (CN) | `https://api.minimax.io/v1` · `https://api.minimaxi.com/v1` | | DeepSeek | `https://api.deepseek.com` | | Cerebras | `https://api.cerebras.ai/v1` | +| DeepInfra | `https://api.deepinfra.com/v1/openai` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | | Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | @@ -226,6 +227,11 @@ validates the key, and stores it. Notable entries: Most use the `openai-chat` adapter with a bearer key; a few that expose only an Anthropic-compatible endpoint (e.g. **Xiaomi MiMo**) use the `anthropic` adapter (`x-api-key`). +**DeepInfra discovery.** The key-based `deepinfra` OpenAI Chat Completions provider uses the +`openai-chat` adapter with a Bearer API key. Its registry-owned model-list URL keeps only rows tagged +`chat`, preserves slash-containing native model ids, and caps live discovery at 512 KiB and 512 raw +rows. Create keys in [DeepInfra's dashboard](https://deepinfra.com/dash/api_keys). + > **Tencent Cloud Coding Plan usage restriction:** Tencent documents this subscription for > interactive coding tools only. General API automation, custom application backends, and > non-interactive batch use are prohibited and may cause the plan key to be suspended. diff --git a/docs-site/src/content/docs/ja/getting-started/quickstart.md b/docs-site/src/content/docs/ja/getting-started/quickstart.md index dbf80359e..ce659483d 100644 --- a/docs-site/src/content/docs/ja/getting-started/quickstart.md +++ b/docs-site/src/content/docs/ja/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init` は次の手順を案内します: -1. **プロバイダー選択** — 組み込みレジストリのプリセット 50 個から一つを選ぶか、`custom` を選んで +1. **プロバイダー選択** — 組み込みレジストリのプリセット 61 個から一つを選ぶか、`custom` を選んで base URL とアダプターを直接入力します。 2. **API キー** — キーを貼り付けるか、`${ANTHROPIC_API_KEY}` のような環境変数を参照します。 3. **デフォルトモデル** — API キー、ローカル、custom プロバイダーではプリセット値を使うかモデル ID を直接入力します。 diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index d24368139..af7c75e3f 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -60,9 +60,10 @@ ChatGPT パススルーカタログには GPT-5.6 Sol/Terra/Luna の名前空間 ## 2. アカウントログイン(OAuth) -OAuth ログインを使うプロバイダープリセットは 6 つです。認証情報は -`~/.opencodex/auth.json` に保存され、自動更新されます。ログイン CLI は `chatgpt` も受け付けます。 -このコマンドは ChatGPT 認証情報を発行し `forward` モードのプロバイダーエントリを作成します。 +OAuth ログインを使うプロバイダープリセットは 6 つで、これに実験的な非公式デバイスフロー +ブリッジ経由の GitHub Copilot が加わります。認証情報は `~/.opencodex/auth.json` に保存され、 +自動更新されます。ログイン CLI は `chatgpt` も受け付けます。このコマンドは ChatGPT 認証情報を +発行し `forward` モードのプロバイダーエントリを作成します。 ```bash ocx login xai # xAI Grok @@ -71,18 +72,20 @@ ocx login kimi # Moonshot Kimi ocx login kiro # kiro-cli 認証情報の取り込み(トークンフォールバック対応) ocx login google-antigravity ocx login cursor # Cursor 専用 PKCE ログイン +ocx login github-copilot # GitHub デバイスフロー → Copilot トークン (Copilot Pro/Business) ocx login chatgpt # 別途 ChatGPT OAuth ログイン ocx logout ``` | プロバイダー | アダプター | ベース URL | 備考 | - --- | --- | --- | --- | +| --- | --- | --- | --- | | `xai` | `openai-chat` | `https://api.x.ai/v1` | ライブ一覧を優先し、フォールバックのデフォルトモデルは `grok-4.5`。 | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude モデル; ライブモデル一覧は `/v1/models` から取得。 | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 コーディングモデル。 | | `kiro` | `kiro` | `https://runtime.us-east-1.kiro.dev` | 初回ログインは、インストール済みでサインインした `kiro-cli` セッションを取り込みます(Unix では `curl -fsSL https://cli.kiro.dev/install | bash`、Windows PowerShell では `irm 'https://cli.kiro.dev/install.ps1' | iex` でインストールしてから `kiro-cli login` を実行)。**アカウントを追加**は `kiro-cli` をログアウトして新しいブラウザログインを開始し、`kiro-cli` 自体のアカウントを切り替えてアカウント別プロファイルメタデータを保存します。既存の OpenCodex アカウントは保持され、キャンセルまたは失敗時には以前の `kiro-cli` セッションが復元されます。 | | `google-antigravity` | `google` | `https://daily-cloudcode-pa.googleapis.com` | Google OAuth を Cloud Code Assist wire で使用。 | | `cursor` | `cursor` | `https://api2.cursor.sh` | 実験的 PKCE ログイン、HTTP/2 トランスポート、アカウント別モデル探索をサポート。 | +| `github-copilot` | `openai-chat` | `https://api.githubcopilot.com` | 実験的。GitHub デバイスフロー + `copilot_internal` 交換(VS Code OAuth クライアント)。有効な Copilot サブスクリプションが必要で、公式のサードパーティ API ではありません。 | 正規の Kimi Coding Plan プリセット(`kimi` アカウントログインと `kimi-code` API key)では、 opencodex は呼び出し元が指定した安定した `prompt_cache_key` だけを Chat Completions リクエストへ @@ -116,7 +119,7 @@ Kiro のログインには Kiro CLI が必要です。Unix では `curl -fsSL ht ## 3. API キーカタログ -opencodex v2.7.1 には組み込みプリセットが 50 個含まれています。キー方式 40、OAuth 6、ローカル 3、 +opencodex には組み込みプリセットが 61 個含まれています。キー方式 50、OAuth 7、ローカル 3、 デフォルト ChatGPT 転送プリセット 1 です。ダッシュボードの **Add provider** ピッカーはキー発行ページを開き、 入力したキーを検証した後保存します。主な項目は以下のとおりです: @@ -133,6 +136,7 @@ opencodex v2.7.1 には組み込みプリセットが 50 個含まれていま | MiniMax · MiniMax (CN) | `https://api.minimax.io/v1` · `https://api.minimaxi.com/v1` | | DeepSeek | `https://api.deepseek.com` | | Cerebras | `https://api.cerebras.ai/v1` | +| DeepInfra | `https://api.deepinfra.com/v1/openai` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | | Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | @@ -152,6 +156,11 @@ opencodex v2.7.1 には組み込みプリセットが 50 個含まれていま 大半は bearer キーと共に `openai-chat` アダプターを使い、Anthropic 互換エンドポイントのみを公開する一部 (例: **Xiaomi MiMo**)は `anthropic` アダプター(`x-api-key`)を使います。 +**DeepInfra の discovery:** キー方式の OpenAI Chat Completions プロバイダー `deepinfra` は、 +`openai-chat` アダプターと Bearer API キーを使います。registry が所有する DeepInfra のモデル一覧 URL から +`chat` タグを持つ行だけを残し、スラッシュを含むネイティブモデル ID を保持します。live discovery は +512 KiB、raw 512 行に制限します。キーは [DeepInfra dashboard](https://deepinfra.com/dash/api_keys) で作成します。 + > **Tencent Cloud Coding Plan の利用制限:** Tencent はこのサブスクリプションを対話型 > コーディングツール専用としています。一般的な API 自動化、カスタムアプリのバックエンド、 > 非対話型バッチ利用は禁止されており、プランキーが停止される場合があります。 diff --git a/docs-site/src/content/docs/ko/getting-started/quickstart.md b/docs-site/src/content/docs/ko/getting-started/quickstart.md index d478de1a2..df6c39f0e 100644 --- a/docs-site/src/content/docs/ko/getting-started/quickstart.md +++ b/docs-site/src/content/docs/ko/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init`은 다음 과정을 안내합니다: -1. **프로바이더 선택** — 내장 레지스트리 프리셋 50개 중 하나를 고르거나, `custom`을 선택해 +1. **프로바이더 선택** — 내장 레지스트리 프리셋 61개 중 하나를 고르거나, `custom`을 선택해 base URL과 adapter를 직접 입력합니다. 2. **API 키** — 키를 붙여넣거나, `${ANTHROPIC_API_KEY}`와 같은 환경 변수를 참조합니다. 3. **기본 모델** — API 키, 로컬, custom 프로바이더에서는 프리셋 값을 쓰거나 모델 id를 직접 입력합니다. diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 34a2fbcb8..e96298b33 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -60,9 +60,10 @@ ChatGPT 패스스루 카탈로그에는 GPT-5.6 Sol/Terra/Luna의 네임스페 ## 2. 계정 로그인 (OAuth) -OAuth 로그인을 사용하는 프로바이더 프리셋은 여섯 개입니다. 자격 증명은 -`~/.opencodex/auth.json`에 저장되고 자동으로 갱신됩니다. 로그인 CLI는 `chatgpt`도 받습니다. -이 명령은 ChatGPT 자격 증명을 발급받고 `forward` 모드 프로바이더 항목을 만듭니다. +OAuth 로그인을 사용하는 프로바이더 프리셋은 여섯 개이며, 여기에 실험적 비공식 디바이스 플로우 +브리지를 쓰는 GitHub Copilot이 추가됩니다. 자격 증명은 `~/.opencodex/auth.json`에 저장되고 +자동으로 갱신됩니다. 로그인 CLI는 `chatgpt`도 받습니다. 이 명령은 ChatGPT 자격 증명을 +발급받고 `forward` 모드 프로바이더 항목을 만듭니다. ```bash ocx login xai # xAI Grok @@ -71,6 +72,7 @@ ocx login kimi # Moonshot Kimi ocx login kiro # kiro-cli 자격 증명 가져오기(토큰 폴백 지원) ocx login google-antigravity ocx login cursor # Cursor 전용 PKCE 로그인 +ocx login github-copilot # GitHub 디바이스 플로우 → Copilot 토큰 (Copilot Pro/Business) ocx login chatgpt # 별도 ChatGPT OAuth 로그인 ocx logout ``` @@ -83,6 +85,7 @@ ocx logout | `kiro` | `kiro` | `https://runtime.us-east-1.kiro.dev` | 최초 로그인은 설치하고 로그인한 `kiro-cli` 세션을 가져옵니다(Unix에서는 `curl -fsSL https://cli.kiro.dev/install | bash`, Windows PowerShell에서는 `irm 'https://cli.kiro.dev/install.ps1' | iex`로 설치한 뒤 `kiro-cli login` 실행). **계정 추가**는 `kiro-cli`에서 로그아웃한 뒤 새 브라우저 로그인을 시작하여 `kiro-cli` 자체의 계정을 전환하고, 계정별 프로필 메타데이터를 저장합니다. 기존 OpenCodex 계정은 유지되며, 취소되거나 실패하면 이전 `kiro-cli` 세션을 복원합니다. | | `google-antigravity` | `google` | `https://daily-cloudcode-pa.googleapis.com` | Google OAuth를 Cloud Code Assist wire로 사용합니다. | | `cursor` | `cursor` | `https://api2.cursor.sh` | 실험적 PKCE 로그인, HTTP/2 전송, 계정별 모델 탐색을 지원합니다. | +| `github-copilot` | `openai-chat` | `https://api.githubcopilot.com` | 실험적. GitHub 디바이스 플로우 + `copilot_internal` 교환(VS Code OAuth 클라이언트). 활성 Copilot 구독 필요; 공식 서드파티 API가 아닙니다. | 정식 Kimi Coding Plan 프리셋(`kimi` 계정 로그인과 `kimi-code` API key)의 경우, opencodex는 호출자가 제공한 안정적인 `prompt_cache_key`만 Chat Completions 요청으로 전달하며 직접 생성하지 @@ -116,7 +119,7 @@ Kiro 로그인에는 Kiro CLI가 필요합니다. Unix에서는 `curl -fsSL http ## 3. API 키 카탈로그 -opencodex v2.7.1에는 빌트인 프리셋이 50개 들어 있습니다. 키 방식 40개, OAuth 6개, 로컬 3개, +opencodex에는 빌트인 프리셋이 61개 들어 있습니다. 키 방식 50개, OAuth 7개, 로컬 3개, 기본 ChatGPT 포워드 프리셋 1개입니다. 대시보드의 **Add provider** 선택기는 키 발급 페이지를 열고, 입력한 키를 검증한 뒤 저장합니다. 주요 항목은 다음과 같습니다: @@ -133,6 +136,7 @@ opencodex v2.7.1에는 빌트인 프리셋이 50개 들어 있습니다. 키 방 | MiniMax · MiniMax (CN) | `https://api.minimax.io/v1` · `https://api.minimaxi.com/v1` | | DeepSeek | `https://api.deepseek.com` | | Cerebras | `https://api.cerebras.ai/v1` | +| DeepInfra | `https://api.deepinfra.com/v1/openai` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | | Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | @@ -152,6 +156,11 @@ opencodex v2.7.1에는 빌트인 프리셋이 50개 들어 있습니다. 키 방 대부분은 bearer 키와 함께 `openai-chat` 어댑터를 사용하며, Anthropic 호환 엔드포인트만 노출하는 일부 (예: **Xiaomi MiMo**)는 `anthropic` 어댑터(`x-api-key`)를 사용합니다. +**DeepInfra 검색:** 키 기반 OpenAI Chat Completions 제공자인 `deepinfra`는 `openai-chat` 어댑터와 +Bearer API 키를 사용합니다. registry가 소유하는 DeepInfra 모델 목록 URL에서 `chat` 태그가 있는 행만 +유지하고, 슬래시가 포함된 네이티브 모델 ID를 보존합니다. live discovery는 512 KiB 및 raw 512행으로 +제한합니다. 키는 [DeepInfra dashboard](https://deepinfra.com/dash/api_keys)에서 생성합니다. + > **Tencent Cloud Coding Plan 사용 제한:** Tencent는 이 구독을 대화형 코딩 도구 전용으로 > 안내합니다. 일반 API 자동화, 사용자 애플리케이션 백엔드 및 비대화형 일괄 호출은 금지되며 > 플랜 키가 정지될 수 있습니다. diff --git a/docs-site/src/content/docs/ru/getting-started/quickstart.md b/docs-site/src/content/docs/ru/getting-started/quickstart.md index 74023c3a0..de1187710 100644 --- a/docs-site/src/content/docs/ru/getting-started/quickstart.md +++ b/docs-site/src/content/docs/ru/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init` проведёт вас по следующим шагам: -1. **Выбор провайдера** — выберите один из 50 встроенных пресетов реестра или `custom`, чтобы +1. **Выбор провайдера** — выберите один из 61 встроенного пресета реестра или `custom`, чтобы ввести базовый URL и адаптер вручную. 2. **API-ключ** — вставьте ключ или сошлитесь на переменную окружения вида `${ANTHROPIC_API_KEY}`. 3. **Модель по умолчанию** — для провайдеров с ключом, локальных и `custom` примите значение из diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index 1d6fa4768..3d9f6626f 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -126,7 +126,7 @@ OAuth-провайдеры, чьи учётные данные содержат ## 3. Каталог API-ключей -opencodex поставляется с 53 встроенными пресетами: 42 на основе ключей, семь OAuth, три локальных и +opencodex поставляется с 61 встроенным пресетом: 50 на основе ключей, семь OAuth, три локальных и пресет ChatGPT-форварда по умолчанию. Селектор **Add provider** в дашборде открывает страницу выдачи ключей провайдера, проверяет ключ и сохраняет его. Наиболее заметные записи: @@ -143,6 +143,7 @@ opencodex поставляется с 53 встроенными пресетам | MiniMax · MiniMax (CN) | `https://api.minimax.io/v1` · `https://api.minimaxi.com/v1` | | DeepSeek | `https://api.deepseek.com` | | Cerebras | `https://api.cerebras.ai/v1` | +| DeepInfra | `https://api.deepinfra.com/v1/openai` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | | Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | @@ -163,6 +164,12 @@ opencodex поставляется с 53 встроенными пресетам только Anthropic-совместимую конечную точку (например, **Xiaomi MiMo**), используют адаптер `anthropic` (`x-api-key`). +**Discovery для DeepInfra.** `deepinfra` — провайдер OpenAI Chat Completions с аутентификацией по +ключу; он использует адаптер `openai-chat` и Bearer API-ключ. Принадлежащий registry URL списка +моделей DeepInfra оставляет только строки с тегом `chat`, сохраняет нативные id моделей со знаком +`/` и ограничивает live discovery 512 KiB и 512 исходными строками. Ключи создаются в +[дашборде DeepInfra](https://deepinfra.com/dash/api_keys). + > **Ограничение Tencent Cloud Coding Plan:** Tencent разрешает использовать эту подписку только > в интерактивных инструментах программирования. Автоматизация общего API, серверы пользовательских > приложений и неинтерактивные пакетные вызовы запрещены и могут привести к блокировке ключа плана. diff --git a/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md b/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md index 4c2f69e49..be5f979d5 100644 --- a/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md +++ b/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init` 会引导你完成: -1. **选择 provider** —— 从内置 registry 的 50 个预设中选择一个,或选择 `custom` 手动输入 +1. **选择 provider** —— 从内置 registry 的 61 个预设中选择一个,或选择 `custom` 手动输入 base URL 和 adapter。 2. **API key** —— 粘贴一个 key,或引用一个环境变量,例如 `${ANTHROPIC_API_KEY}`。 3. **默认模型** —— 对于 API key、本地和 custom provider,可接受预设值或输入模型 id。 diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index 87a31fd05..f20b2c05f 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -55,8 +55,9 @@ ChatGPT 透传目录也会加入 GPT-5.6 Sol/Terra/Luna 的裸 slug(`gpt-5.6-s ## 2. 账号登录(OAuth) -有六个提供商预设使用 OAuth 登录。opencodex 会把凭据存入 `~/.opencodex/auth.json` 并自动刷新。 -登录 CLI 也接受 `chatgpt`:它会获取一份 ChatGPT 凭据,并创建一个 `forward` 模式的提供商条目。 +有六个提供商预设使用 OAuth 登录,另加通过实验性非官方设备流桥接的 GitHub Copilot。 +opencodex 会把凭据存入 `~/.opencodex/auth.json` 并自动刷新。登录 CLI 也接受 `chatgpt`: +它会获取一份 ChatGPT 凭据,并创建一个 `forward` 模式的提供商条目。 ```bash ocx login xai # xAI Grok @@ -65,6 +66,7 @@ ocx login kimi # Moonshot Kimi ocx login kiro # 导入 kiro-cli 凭据(支持令牌回退) ocx login google-antigravity ocx login cursor # 独立的 Cursor PKCE 登录 +ocx login github-copilot # GitHub 设备流 → Copilot 令牌(Copilot Pro/Business) ocx login chatgpt # 独立的 ChatGPT OAuth 登录 ocx logout ``` @@ -77,6 +79,7 @@ ocx logout | `kiro` | `kiro` | `https://runtime.us-east-1.kiro.dev` | 首次登录会导入已安装并已登录的 Kiro CLI 会话(Unix 使用 `curl -fsSL https://cli.kiro.dev/install | bash`;Windows PowerShell 使用 `irm 'https://cli.kiro.dev/install.ps1' | iex`;然后运行 `kiro-cli login`)。**添加账户**会先退出 `kiro-cli`,再启动新的浏览器登录,从而切换 `kiro-cli` 自身使用的账户,并保存账户范围的配置文件元数据。现有 OpenCodex 账户会保留;如果取消或失败,则恢复之前的 `kiro-cli` 会话。 | | `google-antigravity` | `google` | `https://daily-cloudcode-pa.googleapis.com` | 通过 Cloud Code Assist 协议使用 Google OAuth。 | | `cursor` | `cursor` | `https://api2.cursor.sh` | 实验性 PKCE 登录、HTTP/2 传输和按账号筛选的模型发现。 | +| `github-copilot` | `openai-chat` | `https://api.githubcopilot.com` | 实验性。GitHub 设备流 + `copilot_internal` 交换(VS Code OAuth 客户端)。需要有效的 Copilot 订阅;不是官方第三方 API。 | 对于规范的 Kimi Coding Plan 预设(`kimi` 账号登录和 `kimi-code` API key),opencodex 只会把调用方提供的稳定 `prompt_cache_key` 转发到 Chat Completions 请求,绝不自行生成。Kimi @@ -108,7 +111,7 @@ Kiro 登录需要 Kiro CLI:Unix 使用 `curl -fsSL https://cli.kiro.dev/instal ## 3. API 密钥目录 -opencodex v2.7.1 内置 50 个预设:40 个密钥预设、6 个 OAuth 预设、3 个本地预设,以及默认的 +opencodex 内置 61 个预设:50 个密钥预设、7 个 OAuth 预设、3 个本地预设,以及默认的 ChatGPT 转发预设。仪表盘的 **Add provider** 选择器会打开密钥提供商的控制台,验证并保存密钥。 主要条目包括: @@ -125,6 +128,7 @@ ChatGPT 转发预设。仪表盘的 **Add provider** 选择器会打开密钥提 | MiniMax · MiniMax (CN) | `https://api.minimax.io/v1` · `https://api.minimaxi.com/v1` | | DeepSeek | `https://api.deepseek.com` | | Cerebras | `https://api.cerebras.ai/v1` | +| DeepInfra | `https://api.deepinfra.com/v1/openai` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | | Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | @@ -143,6 +147,11 @@ ChatGPT 转发预设。仪表盘的 **Add provider** 选择器会打开密钥提 大多数使用带 bearer 密钥的 `openai-chat` adapter;少数仅暴露 Anthropic 兼容端点的提供商(例如 **Xiaomi MiMo**)使用 `anthropic` adapter(`x-api-key`)。 +**DeepInfra 发现:**`deepinfra` 是使用 `openai-chat` adapter 和 Bearer API 密钥的密钥型 +OpenAI Chat Completions 提供商。registry 固定的 DeepInfra 模型列表 URL 仅保留带 `chat` 标签的记录, +同时保留含 `/` 的原生模型 id,并把实时发现限制为 512 KiB 和 512 条原始记录。 +密钥可在 [DeepInfra 控制台](https://deepinfra.com/dash/api_keys)创建。 + > **腾讯云 Coding Plan 使用限制:**腾讯将此订阅限定为交互式编程工具使用。禁止通用 API > 自动化、自定义应用后端和非交互式批量调用;违规使用可能导致套餐密钥被停用。 diff --git a/src/codex/catalog/provider-fetch.ts b/src/codex/catalog/provider-fetch.ts index b1d95610e..fe8449c99 100644 --- a/src/codex/catalog/provider-fetch.ts +++ b/src/codex/catalog/provider-fetch.ts @@ -345,6 +345,7 @@ export function catalogHintsFromModelsApiItem(providerName: string, item: Provid const contextWindow = positiveSafeInteger( limits?.max_context_length, + metadata?.context_length, item.context_length, item.context_size, item.max_model_len, diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 14f451013..a3ac29870 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -838,6 +838,27 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ }, // llama-3.3-70b was deprecated by Cerebras on 2026-02-16. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md. { id: "cerebras", label: "Cerebras", baseUrl: "https://api.cerebras.ai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://cloud.cerebras.ai/platform/apikeys", defaultModel: "gpt-oss-120b" }, + { + id: "deepinfra", + label: "DeepInfra", + baseUrl: "https://api.deepinfra.com/v1/openai", + adapter: "openai-chat", + authKind: "key", + dashboardUrl: "https://deepinfra.com/dash/api_keys", + liveModels: true, + preserveCustomDestination: true, + modelDiscovery: { + // DeepInfra documents the OpenAI model catalog outside the chat-compatible `/v1/openai` + // namespace, so keep this destination registry-owned instead of deriving it from baseUrl. + url: "https://api.deepinfra.com/v1/models", + maxResponseBytes: 512 * 1024, + maxModels: 512, + filter: { + allOf: [{ path: ["metadata", "tags"], containsAny: ["chat"] }], + }, + }, + note: "OpenAI-compatible chat models only; live discovery excludes non-chat rows from DeepInfra's mixed model catalog.", + }, // FREEZE 2026-07-10: exact serverless ids remain auth-gated/unverified. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md. { id: "together", label: "Together", baseUrl: "https://api.together.xyz/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://api.together.xyz/settings/api-keys" }, { id: "fireworks", label: "Fireworks", baseUrl: "https://api.fireworks.ai/inference/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://fireworks.ai/account/api-keys" }, diff --git a/tests/deepinfra-provider.test.ts b/tests/deepinfra-provider.test.ts new file mode 100644 index 000000000..39385652a --- /dev/null +++ b/tests/deepinfra-provider.test.ts @@ -0,0 +1,204 @@ +import { afterEach, describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { createOpenAIChatAdapter } from "../src/adapters/openai-chat"; +import { gatherRoutedModels } from "../src/codex/catalog"; +import { clearModelCache } from "../src/codex/model-cache"; +import { buildInitProviders } from "../src/cli/init"; +import { buildModelsRequest } from "../src/oauth"; +import { KEY_LOGIN_PROVIDERS, validateApiKey } from "../src/oauth/key-providers"; +import { + deriveInitProviders, + deriveProviderPresets, + providerConfigSeed, +} from "../src/providers/derive"; +import { PROVIDER_REGISTRY, type ProviderRegistryEntry } from "../src/providers/registry"; +import { routedSlug } from "../src/providers/slug-codec"; +import { routeModel } from "../src/router"; +import type { OcxConfig, OcxProviderConfig } from "../src/types"; +import { withStubbedProviderFetch } from "./helpers/catalog-provider-fetch"; + +const FIXTURE = readFileSync(join(import.meta.dir, "fixtures/deepinfra-models.json"), "utf8"); +const BASE_URL = "https://api.deepinfra.com/v1/openai"; +const MODELS_URL = "https://api.deepinfra.com/v1/models"; +const API_KEY = "di-test-key"; +const originalFetch = globalThis.fetch; + +afterEach(() => { + globalThis.fetch = originalFetch; + clearModelCache("deepinfra"); +}); + +function registryEntry(): ProviderRegistryEntry { + const entry = PROVIDER_REGISTRY.find(row => row.id === "deepinfra"); + if (!entry) throw new Error("missing deepinfra registry entry"); + return entry; +} + +function providerConfig(overrides: Partial = {}): OcxConfig { + return { + port: 10100, + defaultProvider: "deepinfra", + providers: { + deepinfra: { + adapter: "openai-chat", + baseUrl: BASE_URL, + authMode: "key", + apiKey: API_KEY, + liveModels: true, + // Discovery stays fixture-only; this avoids platform-specific public-DNS classification. + allowPrivateNetwork: true, + ...overrides, + }, + }, + }; +} + +describe("DeepInfra provider", () => { + test("registers a fixed OpenAI transport with bounded chat-only discovery", () => { + expect(registryEntry()).toMatchObject({ + id: "deepinfra", + label: "DeepInfra", + adapter: "openai-chat", + baseUrl: BASE_URL, + authKind: "key", + dashboardUrl: "https://deepinfra.com/dash/api_keys", + liveModels: true, + preserveCustomDestination: true, + modelDiscovery: { + url: MODELS_URL, + maxResponseBytes: 524_288, + maxModels: 512, + filter: { + allOf: [{ path: ["metadata", "tags"], containsAny: ["chat"] }], + }, + }, + }); + expect(registryEntry().note).toContain("non-chat rows"); + }); + + test("derives CLI and dashboard presets without persisting registry trust policy", () => { + const entry = registryEntry(); + expect(buildInitProviders()).toEqual(deriveInitProviders()); + expect(KEY_LOGIN_PROVIDERS.deepinfra).toMatchObject({ + adapter: "openai-chat", + baseUrl: BASE_URL, + dashboardUrl: entry.dashboardUrl, + liveModels: true, + }); + expect(buildInitProviders().find(row => row.id === "deepinfra")).toMatchObject({ + kind: "key", + adapter: "openai-chat", + baseUrl: BASE_URL, + }); + expect(deriveProviderPresets().find(row => row.id === "deepinfra")).toMatchObject({ + auth: "key", + dashboardUrl: entry.dashboardUrl, + }); + + const seed = providerConfigSeed(entry); + expect(seed).toMatchObject({ + adapter: "openai-chat", + baseUrl: BASE_URL, + authMode: "key", + liveModels: true, + }); + expect(seed).not.toHaveProperty("modelDiscovery"); + expect(seed).not.toHaveProperty("preserveCustomDestination"); + expect(KEY_LOGIN_PROVIDERS.deepinfra).not.toHaveProperty("modelDiscovery"); + expect(KEY_LOGIN_PROVIDERS.deepinfra).not.toHaveProperty("preserveCustomDestination"); + }); + + test("lists and validates models through the documented Bearer-authenticated endpoint", async () => { + expect(buildModelsRequest(providerConfig().providers.deepinfra!, API_KEY, "deepinfra")).toEqual({ + url: MODELS_URL, + headers: { Authorization: `Bearer ${API_KEY}` }, + }); + + globalThis.fetch = (async (input, init) => { + expect(String(input)).toBe(MODELS_URL); + expect(new Headers(init?.headers).get("authorization")).toBe(`Bearer ${API_KEY}`); + expect(init?.redirect).toBe("error"); + return new Response(FIXTURE, { + status: 200, + headers: { "content-type": "application/json" }, + }); + }) as typeof fetch; + + expect(await validateApiKey("deepinfra", KEY_LOGIN_PROVIDERS.deepinfra!, API_KEY)).toBe(true); + }); + + test("filters mixed fixtures, preserves metadata, and routes slash ids in both selector forms", async () => { + globalThis.fetch = (async (input, init) => { + expect(String(input)).toBe(MODELS_URL); + expect(new Headers(init?.headers).get("authorization")).toBeTruthy(); + expect(init?.redirect).toBe("manual"); + return new Response(FIXTURE, { + status: 200, + headers: { "content-type": "application/json" }, + }); + }) as typeof fetch; + + const config = withStubbedProviderFetch(providerConfig()); + const models = (await gatherRoutedModels(config)).filter(row => row.provider === "deepinfra"); + + expect(models.map(row => row.id)).toEqual([ + "deepseek-ai/DeepSeek-V3", + "Qwen/Qwen2.5-VL-72B-Instruct", + ]); + expect(models[0]).toMatchObject({ + owned_by: "deepinfra", + contextWindow: 131_072, + }); + expect(models[1]).toMatchObject({ + owned_by: "deepinfra", + contextWindow: 65_536, + }); + + for (const selector of [ + "deepinfra/deepseek-ai/DeepSeek-V3", + routedSlug("deepinfra", "deepseek-ai/DeepSeek-V3"), + ]) { + expect(routeModel(config, selector).modelId).toBe("deepseek-ai/DeepSeek-V3"); + } + }); + + test("routes chat completions to the fixed inference host", () => { + const route = routeModel(providerConfig(), "deepinfra/deepseek-ai/DeepSeek-V3"); + const request = createOpenAIChatAdapter(route.provider).buildRequest({ + modelId: route.modelId, + context: { messages: [{ role: "user", content: "ping", timestamp: 0 }] }, + stream: true, + options: {}, + }); + const body = JSON.parse(String(request.body)) as Record; + + expect(request.url).toBe(`${BASE_URL}/chat/completions`); + expect(request.headers.Authorization).toBe(`Bearer ${API_KEY}`); + expect(body.model).toBe("deepseek-ai/DeepSeek-V3"); + }); + + test("does not retarget an older same-named custom provider or apply discovery policy", () => { + const customConfig = providerConfig({ baseUrl: "https://custom.example/v1" }); + const route = routeModel(customConfig, "deepinfra/custom-model"); + expect(route.provider).toMatchObject({ + adapter: "openai-chat", + baseUrl: "https://custom.example/v1", + authMode: "key", + }); + expect(buildModelsRequest(customConfig.providers.deepinfra!, "custom-key", "deepinfra")).toEqual({ + url: "https://custom.example/v1/models", + headers: { Authorization: "Bearer custom-key" }, + }); + + const customAdapter = routeModel(providerConfig({ + adapter: "anthropic", + baseUrl: "https://custom.example/anthropic", + }), "deepinfra/custom-model"); + expect(customAdapter.provider).toMatchObject({ + adapter: "anthropic", + baseUrl: "https://custom.example/anthropic", + authMode: "key", + }); + }); +}); diff --git a/tests/fixtures/deepinfra-models.json b/tests/fixtures/deepinfra-models.json new file mode 100644 index 000000000..09304b6d0 --- /dev/null +++ b/tests/fixtures/deepinfra-models.json @@ -0,0 +1,40 @@ +{ + "object": "list", + "data": [ + { + "id": "deepseek-ai/DeepSeek-V3", + "object": "model", + "owned_by": "deepinfra", + "metadata": { + "tags": ["chat"], + "context_length": 131072 + } + }, + { + "id": "Qwen/Qwen2.5-VL-72B-Instruct", + "object": "model", + "owned_by": "deepinfra", + "metadata": { + "tags": ["chat", "vlm", "vision"], + "context_length": 65536 + } + }, + { + "id": "BAAI/bge-large-en-v1.5", + "object": "model", + "owned_by": "deepinfra", + "metadata": { + "tags": ["embed"], + "context_length": 512 + } + }, + { + "id": "black-forest-labs/FLUX-1-schnell", + "object": "model", + "owned_by": "deepinfra", + "metadata": { + "tags": ["image-gen"] + } + } + ] +} diff --git a/tests/provider-registry-parity.test.ts b/tests/provider-registry-parity.test.ts index 9d7d91d40..32fa4acfb 100644 --- a/tests/provider-registry-parity.test.ts +++ b/tests/provider-registry-parity.test.ts @@ -31,7 +31,7 @@ function nativeTemplate(): Record { const EXPECTED_KEY_PROVIDER_IDS = [ "anthropic-apikey", "openai-apikey", "umans", "opencode-go", "neuralwatt", "openrouter", "orcarouter", "bizrouter", "groq", "google", "google-vertex", "azure-openai", - "deepseek", "cerebras", "together", "fireworks", "firepass", "moonshot", + "deepseek", "cerebras", "deepinfra", "together", "fireworks", "firepass", "moonshot", "huggingface", "nvidia", "venice", "zai", "zhipu-bigmodel", "nanogpt", "synthetic", "siliconflow", "qwen-cloud", "tencent-coding-plan", "qianfan", "alibaba", "alibaba-token-plan", "alibaba-token-plan-intl", "parallel", "zenmux", "litellm", "ollama-cloud", "mistral", "minimax", "minimax-cn", "kimi-code", "opencode-zen", "vercel-ai-gateway", diff --git a/tests/server-auth.test.ts b/tests/server-auth.test.ts index 14c434466..36816eb5e 100644 --- a/tests/server-auth.test.ts +++ b/tests/server-auth.test.ts @@ -1897,6 +1897,8 @@ describe("server local API auth", () => { } }); + // Stall past BOUNDED_BODY_TIMEOUT_MS (5s). The old 7s test budget left ~1.9s of + // headroom and timed out on windows-latest under runner contention. test("stalled 400 body timeout never authorizes a pool retry", async () => { const prefix = unsupportedModelBody().slice(0, -1); const suffix = "}"; @@ -1919,7 +1921,7 @@ describe("server local API auth", () => { } finally { await stopPoolRetryHarness(harness); } - }, 7_000); + }, { timeout: SERVER_BUDGET_MS }); test("aborted 400 inspection never authorizes a pool retry", async () => { let releaseDispatch!: () => void;