From d37ac8db31c2302600fe0ab448f372f16eddb083 Mon Sep 17 00:00:00 2001 From: Emmanuel Acheampong Date: Fri, 11 Sep 2026 14:48:27 -0700 Subject: [PATCH] feat(providers): add Crusoe Serverless Inference preset Canonical crusoe API-key preset for https://api.inference.crusoecloud.com/v1 (openai-chat), with live discovery over the authenticated /v1/models capped at 256 KiB and 256 raw rows. The endpoint rejects unauthenticated list requests with 401, so a successful list validates the key. Reasoning stays conservative: reasoningEfforts is empty provider-wide and only openai/gpt-oss-120b is declared as a direct reasoning_effort model; the other reasoning models expose an on/off toggle. parallelToolCalls is off and preserveCustomDestination guards older same-named custom providers. Also adds the signup-credit free-directory row, the dashboard mark with its provenance note, the provider test and fixture, and the base-URL table row, discovery paragraph and preset counts in all eight documentation locales. --- .../docs/fr/getting-started/quickstart.md | 2 +- .../src/content/docs/fr/guides/providers.md | 16 +- .../docs/getting-started/quickstart.md | 2 +- .../src/content/docs/guides/providers.md | 16 +- .../docs/ja/getting-started/quickstart.md | 2 +- .../src/content/docs/ja/guides/providers.md | 16 +- .../docs/ko/getting-started/quickstart.md | 2 +- .../src/content/docs/ko/guides/providers.md | 15 +- .../docs/ru/getting-started/quickstart.md | 2 +- .../src/content/docs/ru/guides/providers.md | 16 +- .../docs/tr/getting-started/quickstart.md | 2 +- .../src/content/docs/tr/guides/providers.md | 16 +- .../docs/zh-cn/getting-started/quickstart.md | 2 +- .../content/docs/zh-cn/guides/providers.md | 13 +- .../docs/zh-tw/getting-started/quickstart.md | 2 +- .../content/docs/zh-tw/guides/providers.md | 13 +- gui/public/provider-icons/README.md | 12 + gui/public/provider-icons/crusoe.svg | 1 + gui/src/provider-icons.ts | 1 + scripts/test-layout/layout.json | 3 +- src/providers/free-directory.ts | 5 +- src/providers/registry.ts | 65 + tests/fixtures/crusoe-models.json | 1387 +++++++++++++++++ tests/fixtures/test-layout-expected.json | 1 + tests/providers/crusoe-provider.test.ts | 286 ++++ .../provider-registry-parity.test.ts | 2 +- 26 files changed, 1880 insertions(+), 20 deletions(-) create mode 100644 gui/public/provider-icons/crusoe.svg create mode 100644 tests/fixtures/crusoe-models.json create mode 100644 tests/providers/crusoe-provider.test.ts diff --git a/docs-site/src/content/docs/fr/getting-started/quickstart.md b/docs-site/src/content/docs/fr/getting-started/quickstart.md index 984294b476..81bb508949 100644 --- a/docs-site/src/content/docs/fr/getting-started/quickstart.md +++ b/docs-site/src/content/docs/fr/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init` vous accompagne dans les étapes suivantes : -1. **Choix d’un fournisseur** — sélectionnez l’un des 79 préréglages intégrés au registre, ou `custom` pour saisir une +1. **Choix d’un fournisseur** — sélectionnez l’un des 80 préréglages intégrés au registre, ou `custom` pour saisir une URL de base et un adaptateur. 2. **Clé API** — collez une clé ou référencez une variable d’environnement telle que `${ANTHROPIC_API_KEY}`. 3. **Modèle par défaut** — pour les fournisseurs clés, locaux et personnalisés, acceptez le préréglage ou saisissez un identifiant de modèle. diff --git a/docs-site/src/content/docs/fr/guides/providers.md b/docs-site/src/content/docs/fr/guides/providers.md index fd696660b7..3ce2d01429 100644 --- a/docs-site/src/content/docs/fr/guides/providers.md +++ b/docs-site/src/content/docs/fr/guides/providers.md @@ -265,7 +265,7 @@ existante n'est pas concernée. ## 3. Catalogue des clés API -opencodex fournit 79 préréglages intégrés : 67 à clé, huit OAuth, trois locaux et un préréglage par défaut de +opencodex fournit 80 préréglages intégrés : 68 à clé, huit OAuth, trois locaux et un préréglage par défaut de transfert ChatGPT. Dans le tableau de bord, le sélecteur **Ajouter un fournisseur** ouvre le tableau de bord du fournisseur à clé, valide la clé et l'enregistre ; la validation dépend du fournisseur. Parmi les entrées notables : @@ -308,6 +308,7 @@ promotionnels de Cline ne sont accessibles que dans l'IDE ou la CLI Cline, pas p | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -445,6 +446,19 @@ de contexte et de modalités d'entrée signalées, et limite la découverte à 5 hôtes de déploiement dédiés sont hors périmètre. Créez des clés dans [Nebius Token Factory](https://tokenfactory.nebius.com). +**Découverte Crusoe.** Le préréglage à clé utilise l'adaptateur `openai-chat` et n'envoie sa clé Bearer +qu'à l'hôte fixe Serverless Inference de Crusoe. `/v1/models` rejette les requêtes non authentifiées +avec un 401, de sorte qu'une liste réussie vaut validation de la clé. La découverte conserve les +identifiants natifs à barre oblique tels que `zai-org/GLM-5.3` et `moonshotai/Kimi-K2.6` exactement comme +Crusoe les renvoie, et est limitée à 256 KiB et 256 lignes brutes. Seules les lignes qui déclarent `is_public: true` et une `architecture.modality` text ou multimodal sont conservées, ce qui exclut les déploiements privés du compte ainsi que les lignes embedding ou média. Les modèles de raisonnement +renvoient leur réflexion dans le champ `reasoning` de Chat Completions, que l'adaptateur lit. Seul +`openai/gpt-oss-120b` accepte une échelle `reasoning_effort` (`low`, `medium`, `high`) ; les autres +modèles de raisonnement traitent ce champ comme un interrupteur, si bien que le préréglage n'annonce +ni échelle d'effort ni appels d'outils parallèles à l'échelle du fournisseur. Les limites de débit +s'appliquent par projet et par modèle (429 en cas de dépassement, 503 pendant la montée en charge d'un +déploiement partagé) et les nouveaux comptes démarrent avec 5 $ de crédits gratuits. Créez une clé dans +la [console Crusoe Cloud](https://console.crusoecloud.com), sous Intelligence Foundry, Inference. + **Découverte DigitalOcean.** Le préréglage utilise une clé d'accès aux modèles avec l'hôte Serverless Inference partagé et fixe, puis croise la réponse `/v1/models` authentifiée avec la liste d'autorisation Chat Completions étayée par la documentation de DigitalOcean. Les identifiants inconnus, limités à Responses, d'embedding ou de diff --git a/docs-site/src/content/docs/getting-started/quickstart.md b/docs-site/src/content/docs/getting-started/quickstart.md index 137b532118..b4570df49e 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 79 built-in registry presets or `custom` to type a base +1. **Pick a provider** — choose one of the 80 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 24bc7c5f3d..c06ad30a2c 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -384,7 +384,7 @@ selectors, then retry. Signing in from a machine with no existing `kiro-cli` ses ## 3. API-key catalog -opencodex ships 79 built-in presets: 67 key-based, eight OAuth, three local, and one default +opencodex ships 80 built-in presets: 68 key-based, eight OAuth, three local, and one default ChatGPT-forward preset. The dashboard's **Add provider** picker opens a key provider's dashboard, validates the key, and stores it; validation is provider-specific. Notable entries: @@ -445,6 +445,7 @@ routing or defaults changes. | Meta Muse Code (CLI credential) | `https://api.meta.ai/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -690,6 +691,19 @@ keeps only rows whose architecture produces text, excluding embedding and image- It preserves slash-containing native ids plus reported context and input-modality metadata, and caps discovery at 512 KiB and 512 raw rows. Dedicated deployment hosts are out of scope. Create keys in [Nebius Token Factory](https://tokenfactory.nebius.com). + +**Crusoe discovery.** The key-based preset uses the `openai-chat` adapter and sends its Bearer key +only to Crusoe's fixed Serverless Inference host. `/v1/models` rejects unauthenticated requests with +401, so a successful list response counts as key validation. Discovery preserves slash-delimited +native ids such as `zai-org/GLM-5.3` and `moonshotai/Kimi-K2.6` exactly as Crusoe returns them and is +capped at 256 KiB and 256 raw rows. Rows are kept only when they report `is_public: true` and a text or multimodal `architecture.modality`, which excludes account-private deployments and any embedding or media rows. Reasoning models return their thinking in the Chat Completions +`reasoning` field, which the adapter reads. Only `openai/gpt-oss-120b` accepts a `reasoning_effort` +ladder (`low`, `medium`, `high`); the other reasoning models treat the field as an on/off toggle, so +the preset declares no provider-wide effort ladder and no provider-wide parallel tool calls. Rate +limits apply per project and per model (429 when exceeded, 503 while a shared deployment scales), and +new accounts start with $5 of free credits. Create a key in the +[Crusoe Cloud console](https://console.crusoecloud.com) under Intelligence Foundry, Inference. + **DigitalOcean discovery.** The preset uses a model access key against the fixed shared Serverless Inference host and intersects the authenticated `/v1/models` response with DigitalOcean's docs-backed Chat Completions allowlist. Unknown, Responses-only, embedding, and media-generation 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 dddd327cbb..357139ee8d 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. **プロバイダーを選択してください** — 79 個の組み込みレジストリプリセットのいずれか、または `custom` を選択してベース URL とアダプターを入力します。 +1. **プロバイダーを選択してください** — 80 個の組み込みレジストリプリセットのいずれか、または `custom` を選択してベース URL とアダプターを入力します。 2. **API キー** — キーを貼り付けるか、`${ANTHROPIC_API_KEY}` のような環境変数を参照します。 3. **デフォルト モデル** — キー、ローカル、カスタム プロバイダーの場合は、プリセットを受け入れるか、モデル ID を入力します。 4. **プロキシ ポート** — デフォルトは `10100` です。 diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index cf2bacebe9..38d2c0e14a 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -169,7 +169,7 @@ Kiro のログインには Kiro CLI が必要です。Unix では `curl -fsSL ht ## 3. API キーカタログ -opencodex には組み込みプリセットが 79 個含まれています。キー方式 67、OAuth 8、ローカル 3、 +opencodex には組み込みプリセットが 80 個含まれています。キー方式 68、OAuth 8、ローカル 3、 デフォルト ChatGPT 転送プリセット 1 です。ダッシュボードの **Add provider** ピッカーはキー発行ページを開き、 入力したキーを検証した後保存します(検証はプロバイダー固有です)。主な項目は以下のとおりです: @@ -210,6 +210,7 @@ Cline IDE/CLI のみで API からは使えません。`minimax/minimax-m2.5` | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -302,6 +303,19 @@ CLI の login flow は公開レスポンスをキーの有効性の証拠にせ ネイティブ ID と、報告された context / input modality metadata を保持し、discovery を 512 KiB と raw 512 行に制限します。dedicated deployment のホストは対象外です。キーは [Nebius Token Factory](https://tokenfactory.nebius.com) で作成します。 + +**Crusoe の discovery:** キー方式のプリセットは `openai-chat` adapter を使用し、Bearer key は +Crusoe の固定 Serverless Inference host にだけ送信します。`/v1/models` は未認証リクエストを 401 で +拒否するため、list の成功を key の検証として扱います。discovery は `zai-org/GLM-5.3` や +`moonshotai/Kimi-K2.6` のようなスラッシュ区切りのネイティブ id を Crusoe が返すままに保持し、256 KiB と +raw 256 行に制限します。`is_public: true` かつ `architecture.modality` が text または multimodal の row だけを残すため、アカウント専用のデプロイや embedding・メディア系の row は除外されます。reasoning model は思考内容を Chat Completions の `reasoning` field で返し、 +adapter はこれを読み取ります。`reasoning_effort` のラダー(`low`、`medium`、`high`)を受け付けるのは +`openai/gpt-oss-120b` のみで、他の reasoning model はこの field をオン/オフの切り替えとして扱うため、 +provider 全体の effort ラダーと parallel tool call は宣伝しません。レート制限は project と model ごとに +適用され(超過時は 429、共有 deployment のスケール中は 503)、新規アカウントには $5 の無料クレジットが +付与されます。キーは [Crusoe Cloud console](https://console.crusoecloud.com) の +Intelligence Foundry > Inference で作成します。 + **DigitalOcean の discovery:** preset は model access key を固定の共有 Serverless Inference ホストで使い、 認証済み `/v1/models` の応答と DigitalOcean の公式ドキュメントで確認した Chat Completions allowlist の 積集合だけを公開します。未知、Responses 専用、embedding、media generation の id は fail closed で除外し、 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 bc83df1b21..7944d81e85 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. **프로바이더 선택** — 내장 레지스트리 프리셋 79개 중 하나를 고르거나 `custom`을 선택해 base URL과 adapter를 직접 입력합니다. +1. **프로바이더 선택** — 내장 레지스트리 프리셋 80개 중 하나를 고르거나 `custom`을 선택해 base URL과 adapter를 직접 입력합니다. 2. **API 키** — 키를 붙여넣거나 `${ANTHROPIC_API_KEY}` 같은 환경 변수를 참조합니다. 3. **기본 모델** — 키, 로컬, custom 프로바이더에서는 프리셋을 그대로 쓰거나 모델 ID를 직접 입력합니다. 4. **프록시 포트** — 기본값은 `10100`입니다. diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 887fdc8b0f..09e3952df1 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -167,7 +167,7 @@ Kiro 로그인에는 Kiro CLI가 필요합니다. Unix에서는 `curl -fsSL http ## 3. API 키 카탈로그 -opencodex에는 빌트인 프리셋이 79개 들어 있습니다. 키 방식 67개, OAuth 8개, 로컬 3개, +opencodex에는 빌트인 프리셋이 80개 들어 있습니다. 키 방식 68개, OAuth 8개, 로컬 3개, 기본 ChatGPT 포워드 프리셋 1개입니다. 대시보드의 **Add provider** 선택기는 키 발급 페이지를 열고, 입력한 키를 검증한 뒤 저장합니다(검증은 프로바이더별로 다릅니다). 주요 항목은 다음과 같습니다: @@ -209,6 +209,7 @@ Cline IDE/CLI에서만 제공되며 API로는 사용할 수 없습니다. `minim | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -299,6 +300,18 @@ SambaStudio deployment 엔드포인트는 범위에서 제외합니다. 키는 보고된 context 및 input modality metadata를 보존하며 discovery를 512 KiB와 raw 행 512개로 제한합니다. dedicated deployment 호스트는 범위에서 제외합니다. 키는 [Nebius Token Factory](https://tokenfactory.nebius.com)에서 생성합니다. + +**Crusoe 검색:** 키 기반 프리셋은 `openai-chat` adapter를 사용하며 Bearer key를 Crusoe의 고정 +Serverless Inference host에만 보냅니다. `/v1/models`는 인증되지 않은 요청을 401로 거부하므로 list 성공을 +key 검증으로 간주합니다. discovery는 `zai-org/GLM-5.3`, `moonshotai/Kimi-K2.6`처럼 슬래시로 구분된 네이티브 +id를 Crusoe가 반환한 그대로 유지하고 256 KiB와 raw 256행으로 제한합니다. `is_public: true`이면서 `architecture.modality`가 text 또는 multimodal인 행만 유지하므로 계정 전용 배포와 embedding, 미디어 행은 제외됩니다. reasoning model은 사고 내용을 +Chat Completions의 `reasoning` field로 반환하며 adapter가 이를 읽습니다. `reasoning_effort` 단계(`low`, +`medium`, `high`)를 받는 것은 `openai/gpt-oss-120b`만이고 다른 reasoning model은 이 field를 on/off +토글로 취급하므로, provider 전체 effort 단계나 parallel tool call은 광고하지 않습니다. rate limit은 +project와 model 단위로 적용되며(초과 시 429, 공유 deployment 확장 중 503) 신규 계정에는 $5 무료 +크레딧이 제공됩니다. 키는 [Crusoe Cloud console](https://console.crusoecloud.com)의 +Intelligence Foundry > Inference에서 생성합니다. + **DigitalOcean 검색:** 프리셋은 model access key를 고정된 공유 Serverless Inference 호스트에 사용하고, 인증된 `/v1/models` 응답과 DigitalOcean 공식 문서로 확인한 Chat Completions allowlist의 교집합만 노출합니다. 알 수 없는 ID, Responses 전용, embedding 및 media-generation ID는 fail closed로 제외하며, 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 20460589f9..e4ec7815ac 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. **Выбор провайдера** — выберите один из 79 встроенных пресетов реестра или `custom`, чтобы +1. **Выбор провайдера** — выберите один из 80 встроенных пресетов реестра или `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 36b94e5cea..63009afa70 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -180,7 +180,7 @@ Inline JSON и лишние позиционные аргументы откло ## 3. Каталог API-ключей -opencodex поставляется с 79 встроенными пресетами: 67 на основе ключей, восемь OAuth, три локальных и +opencodex поставляется с 80 встроенными пресетами: 68 на основе ключей, восемь OAuth, три локальных и один пресет ChatGPT-форварда по умолчанию. Селектор **Add provider** в дашборде открывает страницу выдачи ключей провайдера, проверяет ключ и сохраняет его; проверка зависит от провайдера. Наиболее заметные записи: @@ -223,6 +223,7 @@ opencodex поставляется с 79 встроенными пресетам | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -336,6 +337,19 @@ SambaStudio не входят в область пресета. Ключи со Он сохраняет нативные id со знаком `/`, а также заявленные context и input-modality metadata, и ограничивает discovery размером 512 KiB и 512 исходными строками. Хосты dedicated deployment не входят в область пресета. Ключи создаются в [Nebius Token Factory](https://tokenfactory.nebius.com). + +**Discovery для Crusoe.** Пресет с ключом использует adapter `openai-chat` и отправляет Bearer key +только на фиксированный host Serverless Inference Crusoe. `/v1/models` отвечает 401 на запросы без +ключа, поэтому успешный список считается проверкой ключа. Discovery сохраняет нативные id с косой +чертой, такие как `zai-org/GLM-5.3` и `moonshotai/Kimi-K2.6`, ровно в том виде, в каком их возвращает +Crusoe, и ограничен 256 KiB и 256 исходными строками. Остаются только строки с `is_public: true` и `architecture.modality` text или multimodal, так что приватные для аккаунта развёртывания, а также embedding- и медиа-строки исключаются. Reasoning-модели возвращают рассуждения в поле +`reasoning` Chat Completions, которое adapter читает. Лестницу `reasoning_effort` (`low`, `medium`, +`high`) принимает только `openai/gpt-oss-120b`; остальные reasoning-модели трактуют это поле как +переключатель вкл/выкл, поэтому пресет не заявляет ни provider-wide лестницу effort, ни provider-wide +parallel tool calls. Лимиты действуют на project и model (429 при превышении, 503 пока общий deployment +масштабируется); новые аккаунты получают $5 бесплатных кредитов. Ключ создаётся в +[консоли Crusoe Cloud](https://console.crusoecloud.com) в разделе Intelligence Foundry, Inference. + **Discovery для DigitalOcean.** Пресет использует model access key на фиксированном общем хосте Serverless Inference и публикует только пересечение аутентифицированного ответа `/v1/models` с подтверждённым документацией allowlist для Chat Completions. Неизвестные, Responses-only, diff --git a/docs-site/src/content/docs/tr/getting-started/quickstart.md b/docs-site/src/content/docs/tr/getting-started/quickstart.md index dfff3c6104..e87c9be3f6 100644 --- a/docs-site/src/content/docs/tr/getting-started/quickstart.md +++ b/docs-site/src/content/docs/tr/getting-started/quickstart.md @@ -14,7 +14,7 @@ ocx init `ocx init` adım adım size rehberlik eder: -1. **Bir sağlayıcı seçin** — yerleşik kayıt defterindeki 79 önayardan birini +1. **Bir sağlayıcı seçin** — yerleşik kayıt defterindeki 80 önayardan birini veya bir temel URL ile adaptör yazmak için `custom` seçeneğini belirleyin. 2. **API anahtarı** — bir anahtar yapıştırın veya `${ANTHROPIC_API_KEY}` gibi bir ortam değişkenine başvurun. diff --git a/docs-site/src/content/docs/tr/guides/providers.md b/docs-site/src/content/docs/tr/guides/providers.md index e4bd33bb6f..9c107c5519 100644 --- a/docs-site/src/content/docs/tr/guides/providers.md +++ b/docs-site/src/content/docs/tr/guides/providers.md @@ -300,7 +300,7 @@ olmayan bir makineden oturum açmak bundan etkilenmez. ## 3. API anahtarı kataloğu -opencodex 79 yerleşik önayar ile birlikte gelir: 67 anahtar tabanlı, sekiz +opencodex 80 yerleşik önayar ile birlikte gelir: 68 anahtar tabanlı, sekiz OAuth, üç yerel ve bir varsayılan ChatGPT iletme önayarı. Kontrol panelinin **Sağlayıcı ekle** seçicisi bir anahtar sağlayıcısının kontrol panelini açar, anahtarı doğrular ve saklar; doğrulama sağlayıcıya özgüdür. Dikkate değer @@ -349,6 +349,7 @@ yalnızca Cline IDE/CLI içinde mevcuttur; `minimax/minimax-m2.5` belgelenmiş A | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -481,6 +482,19 @@ keşfi 512 KiB ve 512 ham satırla sınırlar. Özel dağıtım ana bilgisayarla kapsam dışıdır. [Nebius Token Factory](https://tokenfactory.nebius.com) içinde anahtarlar oluşturun. +**Crusoe keşfi.** Anahtar tabanlı önayar `openai-chat` adaptörünü kullanır ve Bearer anahtarını +yalnızca Crusoe'nun sabit Serverless Inference ana bilgisayarına gönderir. `/v1/models` kimliği +doğrulanmamış istekleri 401 ile reddeder, bu nedenle başarılı bir liste yanıtı anahtar doğrulaması +sayılır. Keşif, `zai-org/GLM-5.3` ve `moonshotai/Kimi-K2.6` gibi eğik çizgili yerel kimlikleri Crusoe'nun +döndürdüğü gibi korur ve 256 KiB ile 256 ham satırla sınırlandırılır. Yalnızca `is_public: true` ve text veya multimodal bir `architecture.modality` bildiren satırlar tutulur; hesaba özel dağıtımlar ile embedding veya medya satırları dışlanır. Akıl yürütme modelleri +düşüncelerini Chat Completions `reasoning` alanında döndürür ve adaptör bu alanı okur. +`reasoning_effort` kademelerini (`low`, `medium`, `high`) yalnızca `openai/gpt-oss-120b` kabul eder; +diğer akıl yürütme modelleri bu alanı açma/kapama anahtarı olarak ele alır, bu yüzden önayar sağlayıcı +genelinde effort kademesi veya paralel araç çağrısı tanıtmaz. Hız sınırları proje ve model başına +uygulanır (aşımda 429, paylaşılan dağıtım ölçeklenirken 503) ve yeni hesaplar 5 $ ücretsiz kredi ile +başlar. [Crusoe Cloud konsolunda](https://console.crusoecloud.com) Intelligence Foundry > Inference +altında bir anahtar oluşturun. + **DigitalOcean keşfi.** Önayar, sabit paylaşılan Sunucusuz Çıkarım ana bilgisayarına karşı bir model erişim anahtarı kullanır ve kimlik doğrulamalı `/v1/models` yanıtını DigitalOcean'ın belgelere dayalı Chat Completions izin 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 f4aeabc2d7..a818063bb3 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 的 79 个预设中选择一个,或选择 `custom` 手动输入 base URL 和 adapter。 +1. **选择 provider** — 从内置 registry 的 80 个预设中选择一个,或选择 `custom` 手动输入 base URL 和 adapter。 2. **API key** — 粘贴一个 key,或引用一个环境变量,例如 `${ANTHROPIC_API_KEY}`。 3. **默认模型** — 对于 key、本地和 custom provider,接受预设值或输入模型 id。 4. **代理端口** — 默认为 `10100`。 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 b2aafbcd3a..2df19a021e 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -160,7 +160,7 @@ Kiro 登录需要 Kiro CLI:Unix 使用 `curl -fsSL https://cli.kiro.dev/instal ## 3. API 密钥目录 -opencodex 内置 79 个预设:67 个密钥预设、8 个 OAuth 预设、3 个本地预设,以及 1 个默认的 +opencodex 内置 80 个预设:68 个密钥预设、8 个 OAuth 预设、3 个本地预设,以及 1 个默认的 ChatGPT 转发预设。仪表盘的 **Add provider** 选择器会打开密钥提供商的控制台,验证并保存密钥。 验证因提供商而异。主要条目包括: @@ -201,6 +201,7 @@ Cline IDE/CLI 中提供,不能通过 API 使用;`minimax/minimax-m2.5` 是 | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -326,6 +327,16 @@ ORCAROUTER_BASE_URL=https://router.example ocx login orcarouter-oauth 的记录,从而排除 embedding 和 image-generation 模型。它保留含 `/` 的原生模型 id、上游报告的 context 和 input modality metadata,并将发现限制为 512 KiB 和 512 条原始记录。dedicated deployment 主机不在 范围内。密钥可在 [Nebius Token Factory](https://tokenfactory.nebius.com) 创建。 + +**Crusoe 发现:**密钥预设使用 `openai-chat` adapter,并只向 Crusoe 固定的 Serverless Inference 主机发送 +Bearer key。`/v1/models` 会以 401 拒绝未认证请求,因此成功列出模型即视为密钥验证通过。发现会按 Crusoe +返回的原样保留 `zai-org/GLM-5.3`、`moonshotai/Kimi-K2.6` 这类带斜杠的原生 id,上限为 256 KiB 和 256 条原始记录。只保留 `is_public: true` 且 `architecture.modality` 为 text 或 multimodal 的记录,因此账户私有部署以及 embedding、媒体类记录会被排除。 +推理模型通过 Chat Completions 的 `reasoning` 字段返回思考内容,adapter 会读取该字段。只有 +`openai/gpt-oss-120b` 接受 `reasoning_effort` 档位(`low`、`medium`、`high`),其他推理模型把该字段当作 +开关,因此预设不声明 provider-wide effort 档位,也不声明 provider-wide parallel tool calls。速率限制按 +project 和 model 生效(超限返回 429,共享部署扩容时返回 503),新账户可获得 $5 免费额度。密钥可在 +[Crusoe Cloud 控制台](https://console.crusoecloud.com) 的 Intelligence Foundry > Inference 中创建。 + **DigitalOcean 发现:**该预设使用 model access key 访问固定的共享 Serverless Inference 主机,只公开 已鉴权 `/v1/models` 响应与 DigitalOcean 官方文档确认的 Chat Completions allowlist 的交集。未知、 Responses-only、embedding 和 media-generation 模型 id 会按 fail closed 原则排除。发现上限为 256 KiB diff --git a/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md b/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md index 8236a6f511..7a421e3b1d 100644 --- a/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md +++ b/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md @@ -13,7 +13,7 @@ ocx init `ocx init` 會引導你完成: -1. **選擇 provider** —— 從內建 registry 的 79 個預設中選擇一個,或選擇 `custom` 手動輸入 +1. **選擇 provider** —— 從內建 registry 的 80 個預設中選擇一個,或選擇 `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-tw/guides/providers.md b/docs-site/src/content/docs/zh-tw/guides/providers.md index 72d2ae214a..30c158ab07 100644 --- a/docs-site/src/content/docs/zh-tw/guides/providers.md +++ b/docs-site/src/content/docs/zh-tw/guides/providers.md @@ -226,7 +226,7 @@ database 並移除目前的 WAL、SHM 與 journal sidecar,再發布先前的 s ## 3. API 金鑰目錄 -opencodex 內建 79 個 preset:67 個 key-based、8 個 OAuth、3 個 local,以及 1 個預設 ChatGPT-forward +opencodex 內建 80 個 preset:68 個 key-based、8 個 OAuth、3 個 local,以及 1 個預設 ChatGPT-forward preset。儀表板的 **Add provider** picker 會開啟 key provider 的 dashboard、驗證金鑰並儲存;驗證方式 依 provider 而異。主要條目如下。 @@ -267,6 +267,7 @@ IDE/CLI,不透過 API;`minimax/minimax-m2.5` 是文件列出的 API 免費 | Command Code | `https://api.commandcode.ai/provider/v1` | | SambaNova Cloud | `https://api.sambanova.ai/v1` | | Nebius Token Factory | `https://api.tokenfactory.nebius.com/v1` | +| Crusoe | `https://api.inference.crusoecloud.com/v1` | | DigitalOcean Serverless Inference | `https://inference.do-ai.run/v1` | | Scaleway Generative APIs | `https://api.scaleway.ai/v1` | | Featherless AI | `https://api.featherless.ai/v1` | @@ -375,6 +376,16 @@ text 的 row,排除 embedding 與 image-generation model。它保留含 `/` input-modality metadata,並把 discovery 限制在 512 KiB/512 個 raw row。Dedicated deployment host 不在 範圍內。可在 [Nebius Token Factory](https://tokenfactory.nebius.com) 建立 key。 +**Crusoe 探索。** key-based preset 使用 `openai-chat` adapter,只把 Bearer key 傳到 Crusoe 固定的 +Serverless Inference host。`/v1/models` 會以 401 拒絕未驗證的請求,因此成功列出 model 即視為 key 驗證通過。 +discovery 會依 Crusoe 回傳的形式完整保留 `zai-org/GLM-5.3`、`moonshotai/Kimi-K2.6` 這類含 `/` 的原生 id, +並限制在 256 KiB/256 個 raw row。只保留 `is_public: true` 且 `architecture.modality` 為 text 或 multimodal 的 row,因此帳戶私有部署以及 embedding、媒體類 row 會被排除。reasoning model 會透過 Chat Completions 的 `reasoning` 欄位回傳思考內容, +adapter 會讀取該欄位。只有 `openai/gpt-oss-120b` 接受 `reasoning_effort` 等級(`low`、`medium`、`high`), +其他 reasoning model 把該欄位當作開關,因此 preset 不宣告 provider-wide effort 等級,也不宣告 +provider-wide parallel tool call。rate limit 以 project 與 model 為單位(超過時回傳 429,共用 deployment +擴容時回傳 503),新帳戶可獲得 $5 免費額度。可在 [Crusoe Cloud console](https://console.crusoecloud.com) +的 Intelligence Foundry > Inference 建立 key。 + **DigitalOcean 探索。** preset 以 model access key 存取固定的 shared Serverless Inference host,並把經 認證的 `/v1/models` response 與 DigitalOcean 文件支持的 Chat Completions allowlist 取交集。未知、 Responses-only、embedding 與 media-generation id 都 fail closed。discovery 限制在 256 KiB/256 個 raw diff --git a/gui/public/provider-icons/README.md b/gui/public/provider-icons/README.md index 793190db02..e9ba61d70b 100644 --- a/gui/public/provider-icons/README.md +++ b/gui/public/provider-icons/README.md @@ -365,3 +365,15 @@ This one needs to stay written down. The wiring test only fires when an asset named after the provider id is already committed, so an absent mark produces no signal at all — nothing would stop a later pass from fetching that logo and committing it. + +## Provider marks (2026-09-11) + +- `crusoe.svg` — the Crusoe lozenge with its four-stop brand gradient (`#F4BF45`, + `#E48047`, `#C73361`, `#A42F5F`), `viewBox="0 0 24 24"`. Crusoe publishes no + square SVG of its own: `crusoe.ai` serves a 32px PNG favicon from Webflow and + the only vendor SVG in the wild is the 121x30 wordmark on models.dev. The file + is `icons/crusoe-color.svg` from `@lobehub/icons-static-svg` 1.95.0 (MIT), + fetched 2026-09-11 from jsDelivr and committed unmodified (628 bytes, MD5 + `b60df52303ba7170772b256c20c04940`), gradient id and all. The contributor works + at Crusoe and confirms this is the company mark. Painted as an image: the + gradient is the brand, so it must never be masked. diff --git a/gui/public/provider-icons/crusoe.svg b/gui/public/provider-icons/crusoe.svg new file mode 100644 index 0000000000..69540a71dd --- /dev/null +++ b/gui/public/provider-icons/crusoe.svg @@ -0,0 +1 @@ +Crusoe \ No newline at end of file diff --git a/gui/src/provider-icons.ts b/gui/src/provider-icons.ts index 606723a368..74130bf03b 100644 --- a/gui/src/provider-icons.ts +++ b/gui/src/provider-icons.ts @@ -66,6 +66,7 @@ const PROVIDER_ICON_ALIASES: Record = { baseten: "baseten.svg", bizrouter: "bizrouter.svg", cerebras: "cerebras.svg", + crusoe: "crusoe.svg", deepinfra: "deepinfra.svg", digitalocean: "digitalocean.svg", featherless: "featherless.svg", diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index 4f890cba99..a64c597e55 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -11,7 +11,7 @@ "domains": { "providers": { "match": [ - "^(?:aside(?!-profile)|auto|azure|baseten|chutes|cline(?!-(?:client|writer))|command|commandcode|context(?!-compat|-history)|cyber|deepinfra|deepseek|digitalocean|exa|featherless|forward|hyperbolic|kimi|meta|mimo|minimax|moonshot|muse|new|nous|novita|nscale|nvidia|opencode|openrouter|qwen38|sambanova|umans|vercel|zcode|zhipu)-" + "^(?:aside(?!-profile)|auto|azure|baseten|chutes|cline(?!-(?:client|writer))|command|commandcode|context(?!-compat|-history)|crusoe|cyber|deepinfra|deepseek|digitalocean|exa|featherless|forward|hyperbolic|kimi|meta|mimo|minimax|moonshot|muse|new|nous|novita|nscale|nvidia|opencode|openrouter|qwen38|sambanova|umans|vercel|zcode|zhipu)-" ], "children": { "cursor": [ @@ -554,6 +554,7 @@ "cost-scoring.test.ts": "usage", "crash-guard.test.ts": "service", "credential-redirect-guard.test.ts": "lib", + "crusoe-provider.test.ts": "providers", "cursor-adapter.test.ts": "providers/cursor", "cursor-continuity-retention.test.ts": "providers/cursor", "cursor-arg-normalize.test.ts": "providers/cursor", diff --git a/src/providers/free-directory.ts b/src/providers/free-directory.ts index c378f16146..a22a4ca63c 100644 --- a/src/providers/free-directory.ts +++ b/src/providers/free-directory.ts @@ -18,7 +18,7 @@ export const FREE_PROVIDER_ACCESS_GROUPS = { ], "recurring-credit": ["bytez", "nous-research"], "signup-credit": [ - "agentrouter", "ai21", "baichuan", "baseten", "deepinfra", "deepseek", "doubao", "fireworks", "freemodel-dev", "glm-cn", + "agentrouter", "ai21", "baichuan", "baseten", "crusoe", "deepinfra", "deepseek", "doubao", "fireworks", "freemodel-dev", "glm-cn", "hyperbolic", "longcat", "monsterapi", "nebius", "novita", "nscale", "nvidia", "predibase", "publicai", "qoder", "qoder-cn", "scaleway", "sensenova", "stepfun", "together", "vertex", ], @@ -127,6 +127,7 @@ const CONNECTABLE: Record = { // Verified end-to-end 2026-07-30: /v1/models returns the OpenAI-shaped live catalog (13 models), // and a chat completion against moonshotai/Kimi-K3 returned a standard chat.completion payload. baseten: openAi("https://inference.baseten.co/v1", "https://app.baseten.co/settings/api_keys", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.baseten.co/inference/model-apis/overview", modelsUrl: "https://inference.baseten.co/v1/models", lastVerified: "2026-07-30" }), + crusoe: openAi("https://api.inference.crusoecloud.com/v1", "https://console.crusoecloud.com", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.crusoecloud.com/quickstart/getting-started-with-serverless-inference", modelsUrl: "https://api.inference.crusoecloud.com/v1/models", lastVerified: "2026-09-11" }), deepinfra: openAi("https://api.deepinfra.com/v1/openai", "https://deepinfra.com/dash/api_keys", { supportLevel: "supported", verification: "official", documentationUrl: "https://deepinfra.com/docs/openai_api" }), deepseek: openAi("https://api.deepseek.com", "https://platform.deepseek.com/api_keys", { supportLevel: "supported", verification: "official", documentationUrl: "https://api-docs.deepseek.com/api/list-models" }), doubao: openAi("https://ark.cn-beijing.volces.com/api/v3", "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey", { verification: "official" }), @@ -181,7 +182,7 @@ const LABELS: Record = { "t3-web": "T3 Web", uncloseai: "UncloseAI", ainative: "AI Native", baidu: "Baidu Qianfan", glm: "Z.AI GLM", "glm-cn": "BigModel GLM (CN)", "kilo-gateway": "Kilo Gateway", "opencode-zen": "OpenCode Zen", sealion: "SEA-LION", bytez: "Bytez", "nous-research": "Nous Research", agentrouter: "AgentRouter", - ai21: "AI21", baichuan: "Baichuan", deepinfra: "DeepInfra", deepseek: "DeepSeek", doubao: "Doubao", + ai21: "AI21", baichuan: "Baichuan", crusoe: "Crusoe", deepinfra: "DeepInfra", deepseek: "DeepSeek", doubao: "Doubao", "freemodel-dev": "FreeModel.dev", sambanova: "SambaNova Cloud", nebius: "Nebius Token Factory", novita: "Novita", nscale: "Nscale", nvidia: "NVIDIA NIM", publicai: "PublicAI", qoder: "Qoder", "qoder-cn": "Qoder CN", sensenova: "SenseNova", stepfun: "StepFun", vertex: "Google Vertex AI", diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 08096f0fb7..d62fccb785 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -2496,6 +2496,71 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ }, note: "Shared Token Factory text-output inference only; live discovery excludes embedding and image-generation rows.", }, + { + // Primary sources checked 2026-09-11: + // - https://docs.crusoecloud.com/quickstart/getting-started-with-serverless-inference documents + // the fixed OpenAI-compatible host https://api.inference.crusoecloud.com/v1, Bearer API keys + // created in the Cloud console (Intelligence Foundry > Inference > Create API Key), and an + // OpenAI SDK chat.completions example against meta-llama/Llama-3.3-70B-Instruct. + // - https://docs.crusoecloud.com/serverless-inference/available-models lists the served models + // with slash-delimited ids; https://docs.crusoecloud.com/serverless-inference/rate-limits + // documents per-project, per-model TPM/RPM limits (429 when exceeded, 503 under shared load). + // - GET /v1/models rejects unauthenticated requests with 401 {"errors":["Authentication failed"]}, + // so a successful authenticated list response is evidence that the supplied key is valid. + // An authenticated capture on 2026-09-12 returned 18 rows shaped like OpenRouter's catalog + // (`is_public`, `type`, `context_length`, `architecture.modality` of "text" or "multimodal", + // `tags`, `pricing`, `supported_parameters`); 17 were public serverless models and one was an + // account-private dedicated deployment with empty `type`/`modality`. `type` is blank on one + // public model, so the filter keys on `is_public` plus `architecture.modality` instead. + // - https://legal.crusoe.ai/ hosts the Crusoe Cloud Platform Terms of Service v1.10 (effective + // 2026-08-10), which name Crusoe Technologies LLC as the contracting entity, and the Service + // Specific Terms v5.0 (effective 2026-07-14), whose Crusoe Intelligence Foundry Terms cover the + // Managed Inference Service reached through the Crusoe API. + // - https://models.dev/api.json (provider "crusoe") records openai/gpt-oss-120b as the one served + // model with a low/medium/high reasoning_effort ladder; the other reasoning models expose an + // on/off toggle only. + // Maintainer: @acheamponge, who works at Crusoe (affiliation disclosed) and also maintains the + // models.dev crusoe entry. + id: "crusoe", + label: "Crusoe", + baseUrl: "https://api.inference.crusoecloud.com/v1", + adapter: "openai-chat", + authKind: "key", + dashboardUrl: "https://console.crusoecloud.com", + liveModels: true, + preserveCustomDestination: true, + // The getting-started guide documents tools through the OpenAI SDK but no provider-wide + // parallel tool-call contract. + parallelToolCalls: false, + // Only gpt-oss-120b has a real effort ladder; toggle-style reasoning models must not be promoted + // to Codex's full fallback ladder. + reasoningEfforts: [], + modelReasoningEfforts: { "openai/gpt-oss-120b": ["low", "medium", "high"] }, + directReasoningEffortModels: ["openai/gpt-oss-120b"], + // The catalog reports `architecture.modality: "multimodal"` without an input list, so the + // image-capable rows (tagged "image text to text" in the same capture) are classified here. + modelInputModalities: { + "google/gemma-4-31b-it": ["text", "image"], + "moonshotai/Kimi-K2.6": ["text", "image"], + "nvidia/Nemotron-3-Nano-Omni-Reasoning-30B-A3B": ["text", "image"], + "yutori/n2": ["text", "image"], + "zai-org/GLM-5.3-Flash": ["text", "image"], + }, + modelDiscovery: { + path: "models", + maxResponseBytes: 256 * 1024, + maxModels: 256, + filter: { + // Keep public serverless rows whose architecture produces text; account-private + // deployments (blank modality) and any embedding or media rows fail closed. + allOf: [ + { path: ["is_public"], equalsAny: [true] }, + { path: ["architecture", "modality"], equalsAny: ["text", "multimodal"] }, + ], + }, + }, + note: "Public Serverless Inference chat models on the shared OpenAI-compatible host; account-private and self-serve dedicated deployments are excluded from discovery and out of scope.", + }, { id: "digitalocean", label: "DigitalOcean Serverless Inference", diff --git a/tests/fixtures/crusoe-models.json b/tests/fixtures/crusoe-models.json new file mode 100644 index 0000000000..f46b567922 --- /dev/null +++ b/tests/fixtures/crusoe-models.json @@ -0,0 +1,1387 @@ +{ + "object": "list", + "data": [ + { + "id": "openai/gpt-oss-120b", + "name": "openai/gpt-oss-120b", + "created": 0, + "description": "GPT-OSS-120B is an open-weight large language model developed by OpenAI and released on August 5, 2025. Designed for high-performance reasoning, agentic workflows, and broad general-purpose use, it offers developers a flexible, powerful foundation for building advanced AI systems and applications.", + "icon": "https://huggingface.co/api/organizations/openai/avatar", + "owned_by": "openai", + "is_public": true, + "type": "chat", + "context_length": 131072, + "quantization": "bf16", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "chat", + "parameter_count": 120412337472 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.05", + "completion": "0.25", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.05" + }, + "top_provider": { + "context_length": 131072, + "max_completion_tokens": 131072, + "is_moderated": false + } + }, + { + "id": "google/gemma-4-31b-it", + "name": "google/gemma-4-31b-it", + "created": 0, + "description": "Model by google - (image-text-to-text) - 32.7B parameters", + "icon": "https://huggingface.co/api/organizations/google/avatar", + "owned_by": "google", + "is_public": true, + "type": "multimodal", + "context_length": 262144, + "quantization": "", + "architecture": { + "modality": "multimodal", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 32682372656 + }, + "tags": [ + "text to text", + "image text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.14", + "completion": "0.40", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.14" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262141, + "is_moderated": false + } + }, + { + "id": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B", + "name": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B", + "created": 0, + "description": "Nemotron-3-Super-120B-A12B is a 120 billion parameter Mixture-of-Experts (MoE) language model trained by NVIDIA. This variation of the Nemotron 3 family employs a unique MoE architecture that improves accuracy and efficiency of long-form text generation without limiting throughput or latency. Optimized for multi-agent collaboration and high volume inference workloads.", + "icon": "https://huggingface.co/api/organizations/nvidia/avatar", + "owned_by": "nvidia", + "is_public": true, + "type": "chat", + "context_length": 262144, + "quantization": "fp8", + "architecture": { + "modality": "text", + "tokenizer": "", + "instruct_type": "", + "parameter_count": 123611012096 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.30", + "completion": "2.40", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.15" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262144, + "is_moderated": false + } + }, + { + "id": "example/private-deployment", + "name": "example/private-deployment", + "created": 0, + "description": "Fixture-only stand-in for an account-private dedicated deployment.", + "icon": "", + "owned_by": "fixture", + "is_public": false, + "type": "", + "context_length": 262144, + "quantization": "bf16", + "architecture": { + "modality": "", + "tokenizer": "", + "instruct_type": "", + "parameter_count": 0 + }, + "tags": [], + "supported_parameters": [], + "is_billed_by_token": false, + "pricing": { + "prompt": "0", + "completion": "0", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 0, + "is_moderated": false + } + }, + { + "id": "deepseek-ai/DeepSeek-V3-0324", + "name": "deepseek-ai/DeepSeek-V3-0324", + "created": 0, + "description": "DeepSeek‑V3‑0324 is an open-weight large language model developed by DeepSeek and released on March 24, 2025. This mid-cycle update to DeepSeek V3 retains its core architecture while introducing optimizations that enhance reasoning, coding, math, and writing. With 671 billion total parameters and a Mixture-of-Experts (MoE) design activating 37 billion parameters per token, DeepSeek‑V3‑0324 delivers a powerful and efficient foundation for advanced AI applications.", + "icon": "https://huggingface.co/api/organizations/deepseek-ai/avatar", + "owned_by": "deepseek", + "is_public": true, + "type": "chat", + "context_length": 163840, + "quantization": "bf16", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 684531386000 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.50", + "completion": "1.50", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.25" + }, + "top_provider": { + "context_length": 163840, + "max_completion_tokens": 163840, + "is_moderated": false + } + }, + { + "id": "deepseek-ai/DeepSeek-V4-Pro", + "name": "deepseek-ai/DeepSeek-V4-Pro", + "created": 0, + "description": "The DeepSeek-V4 series includes two strong Mixture-of-Experts (MoE) language models — DeepSeek-V4-Pro with 1.6T parameters (49B activated) and DeepSeek-V4-Flash with 284B parameters (13B activated) — both supporting a context length of one million tokens. Both models are pre-trained on more than 32T diverse and high-quality tokens, followed by a comprehensive post-training pipeline. The DeepSeek-V4 series includes two strong Mixture-of-Experts (MoE) language models — DeepSeek-V4-Pro with 1.6T parameters (49B activated) and DeepSeek-V4-Flash with 284B parameters (13B activated) — both supporting a context length of one million tokens. Both models are pre-trained on more than 32T diverse and high-quality tokens, followed by a comprehensive post-training pipeline. The post-training features a two-stage paradigm: independent cultivation of domain-specific experts (through SFT and RL with GRPO), followed by unified model consolidation via on-policy distillation, integrating distinct proficiencies across diverse domains into a single model.", + "icon": "https://huggingface.co/api/organizations/deepseek-ai/avatar", + "owned_by": "deepseek", + "is_public": true, + "type": "chat", + "context_length": 1048576, + "quantization": "fp8", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 861608274846 + }, + "tags": [ + "text to text", + "tool calling" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "1.74", + "completion": "3.48", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.15" + }, + "top_provider": { + "context_length": 1048576, + "max_completion_tokens": 1048576, + "is_moderated": false + } + }, + { + "id": "meta-llama/Llama-3.3-70B-Instruct", + "name": "meta-llama/Llama-3.3-70B-Instruct", + "created": 0, + "description": "Llama 3.3 70B Instruct is a multilingual, instruction-tuned large language model developed by Meta AI and released in December 2024. This update to Llama 3.1 70B builds on its predecessor with enhancements in reasoning, tool use, math, code generation, and multilingual capabilities. Optimized for conversational AI, code assistance, agentic systems, enterprise search, RAG workflows, and multilingual tool use across eight languages, Llama 3.3 70B Instruct delivers industry-leading performance comparable to Llama 3.1 405B while offering significant improvements in speed and efficiency.", + "icon": "https://huggingface.co/api/organizations/meta-llama/avatar", + "owned_by": "meta-llama", + "is_public": true, + "type": "chat", + "context_length": 131072, + "quantization": "bf16", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 70553706496 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.25", + "completion": "0.75", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.13" + }, + "top_provider": { + "context_length": 131072, + "max_completion_tokens": 131072, + "is_moderated": false + } + }, + { + "id": "zai-org/GLM-5.3", + "name": "zai-org/GLM-5.3", + "created": 1788691601, + "description": "Model by zai-org - (text-generation) - 753.3B parameters", + "icon": "https://huggingface.co/api/organizations/zai-org/avatar", + "owned_by": "zai-org", + "is_public": true, + "type": "chat", + "context_length": 1048576, + "quantization": "fp4", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 753329940480 + }, + "tags": [ + "text to text", + "tool calling" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "ebnf", + "frequency_penalty", + "ignore_eos", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "min_tokens", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "regex", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "skip_special_tokens", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "1.40", + "completion": "4.40", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.26" + }, + "top_provider": { + "context_length": 1048576, + "max_completion_tokens": 0, + "is_moderated": false + } + }, + { + "id": "zai-org/GLM-5.3-Flash", + "name": "zai-org/GLM-5.3-Flash", + "created": 1788444748, + "description": "Model by zai-org - (image-text-to-text) - 321.3B parameters", + "icon": "https://huggingface.co/api/organizations/zai-org/avatar", + "owned_by": "zai-org", + "is_public": true, + "type": "multimodal", + "context_length": 1048576, + "quantization": "fp4", + "architecture": { + "modality": "multimodal", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 321323031390 + }, + "tags": [ + "image text to text", + "text to text", + "tool calling" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.15", + "completion": "0.50", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.03" + }, + "top_provider": { + "context_length": 1048576, + "max_completion_tokens": 1048576, + "is_moderated": false + } + }, + { + "id": "yutori/n2", + "name": "yutori/n2", + "created": 1787766459, + "description": "n2 is Yutori's flagship computer-use model that reliably interleaves desktop apps, browsers, CLIs, and short snippets of code to complete tasks end-to-end. Built to power agentic knowledge workflows at scale.", + "icon": "https://yutori.com/_next/image?url=%2Fblog%2Fintroducing-navigator%2FnavigatorCoverSML2.png&w=1920&q=75", + "owned_by": "yutori", + "is_public": true, + "type": "multimodal", + "context_length": 262144, + "quantization": "bf16", + "architecture": { + "modality": "multimodal", + "tokenizer": "", + "instruct_type": "chat", + "parameter_count": 27781427952 + }, + "tags": [ + "tool calling", + "browser use", + "computer use" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.50", + "completion": "4.00", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.05" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262143, + "is_moderated": false + } + }, + { + "id": "nvidia/Nemotron-3.5-Lightning-30B-A3B", + "name": "nvidia/Nemotron-3.5-Lightning-30B-A3B", + "created": 1785934910, + "description": "Nemotron 3.5 Lightning", + "icon": "https://huggingface.co/api/organizations/nvidia/avatar", + "owned_by": "nvidia", + "is_public": true, + "type": "", + "context_length": 262144, + "quantization": "bf16", + "architecture": { + "modality": "text", + "tokenizer": "", + "instruct_type": "", + "parameter_count": 32921182784 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "ignore_eos", + "include", + "include_stop_str_in_output", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "min_tokens", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user", + "verbosity" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.05", + "completion": "0.20", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.03" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262143, + "is_moderated": false + } + }, + { + "id": "zai/GLM-5.2", + "name": "zai/GLM-5.2", + "created": 1784630005, + "description": "Model by crusoeai - (text-generation) - 379.2B parameters", + "icon": "https://huggingface.co/api/organizations/crusoeai/avatar", + "owned_by": "zai-org", + "is_public": true, + "type": "chat", + "context_length": 1048576, + "quantization": "fp8", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 753329940480 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "1.40", + "completion": "4.40", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.26" + }, + "top_provider": { + "context_length": 1048576, + "max_completion_tokens": 1048576, + "is_moderated": false + } + }, + { + "id": "moonshotai/Kimi-K2.6", + "name": "moonshotai/Kimi-K2.6", + "created": 1782138302, + "description": "Model by moonshotai - (image-text-to-text) - 1058.6B parameters", + "icon": "https://huggingface.co/api/organizations/moonshotai/avatar", + "owned_by": "moonshotai", + "is_public": true, + "type": "multimodal", + "context_length": 262144, + "quantization": "bf16", + "architecture": { + "modality": "multimodal", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 1058589420528 + }, + "tags": [ + "text to text", + "image text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.70", + "completion": "3.50", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.35" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 0, + "is_moderated": false + } + }, + { + "id": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "Qwen/Qwen3-235B-A22B-Instruct-2507", + "created": 0, + "description": "Qwen3-235B-A22B-Instruct-2507 is an instruction-tuned large language model developed by Alibaba’s Qwen team and released on July 25, 2025. Designed for complex reasoning, instruction following, coding, long-context comprehension, and multilingual creative writing, it provides developers a versatile foundation for building high-performance AI applications.", + "icon": "https://huggingface.co/api/organizations/Qwen/avatar", + "owned_by": "Qwen", + "is_public": true, + "type": "chat", + "context_length": 262144, + "quantization": "bf16", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 235093634560 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.22", + "completion": "0.80", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.11" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262144, + "is_moderated": false + } + }, + { + "id": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", + "name": "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", + "created": 0, + "description": "Nemotron-3-Nano-30B-A3B is a 30 billion parameter Mixture-of-Experts (MoE) language model trained by NVIDIA. Designed for agentic, chatbot, and RAG systems, it balances the capability of a large model with the efficiency of a much smaller one, optimizing across performance and compute efficiency. This model can be configured to support both reasoning and non-reasoning tasks in English, German, Spanish, French, Italian, and Japanese. ", + "icon": "https://huggingface.co/api/organizations/nvidia/avatar", + "owned_by": "nvidia", + "is_public": true, + "type": "chat", + "context_length": 262144, + "quantization": "fp8", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 31577946256 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.05", + "completion": "0.20", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.03" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262144, + "is_moderated": false + } + }, + { + "id": "zai/GLM-5.1", + "name": "zai/GLM-5.1", + "created": 1784177512, + "description": "Model by zai-org - (text-generation) - 753.9B parameters", + "icon": "https://huggingface.co/api/organizations/zai-org/avatar", + "owned_by": "zai-org", + "is_public": true, + "type": "chat", + "context_length": 202752, + "quantization": "fp8", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 753910024032 + }, + "tags": [ + "text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "1.20", + "completion": "4.40", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.25" + }, + "top_provider": { + "context_length": 202752, + "max_completion_tokens": 202752, + "is_moderated": false + } + }, + { + "id": "nvidia/Nemotron-3-Nano-Omni-Reasoning-30B-A3B", + "name": "nvidia/Nemotron-3-Nano-Omni-Reasoning-30B-A3B", + "created": 0, + "description": "Model by Nvidia - 33.0B parameters", + "icon": "https://huggingface.co/api/organizations/nvidia/avatar", + "owned_by": "nvidia", + "is_public": true, + "type": "chat", + "context_length": 262144, + "quantization": "bf16", + "architecture": { + "modality": "multimodal", + "tokenizer": "", + "instruct_type": "", + "parameter_count": 33015632214 + }, + "tags": [ + "text to text", + "image text to text" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.30", + "completion": "1.83", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.30" + }, + "top_provider": { + "context_length": 262144, + "max_completion_tokens": 262144, + "is_moderated": false + } + }, + { + "id": "deepseek-ai/Deepseek-V4-Flash", + "name": "deepseek-ai/Deepseek-V4-Flash", + "created": 0, + "description": "The DeepSeek-V4 series includes two strong Mixture-of-Experts (MoE) language models — DeepSeek-V4-Pro with 1.6T parameters (49B activated) and DeepSeek-V4-Flash with 284B parameters (13B activated) — both supporting a context length of one million tokens. Both models are pre-trained on more than 32T diverse and high-quality tokens, followed by a comprehensive post-training pipeline. The DeepSeek-V4 series includes two strong Mixture-of-Experts (MoE) language models — DeepSeek-V4-Pro with 1.6T parameters (49B activated) and DeepSeek-V4-Flash with 284B parameters (13B activated) — both supporting a context length of one million tokens. Both models are pre-trained on more than 32T diverse and high-quality tokens, followed by a comprehensive post-training pipeline. The post-training features a two-stage paradigm: independent cultivation of domain-specific experts (through SFT and RL with GRPO), followed by unified model consolidation via on-policy distillation, integrating distinct proficiencies across diverse domains into a single model.", + "icon": "https://huggingface.co/api/organizations/deepseek-ai/avatar", + "owned_by": "deepseek", + "is_public": true, + "type": "chat", + "context_length": 1048576, + "quantization": "fp8", + "architecture": { + "modality": "text", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 158069433298 + }, + "tags": [ + "text to text", + "tool calling" + ], + "supported_parameters": [ + "background", + "chat_template_kwargs", + "conversation", + "frequency_penalty", + "include", + "instructions", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_output_tokens", + "max_tokens", + "max_tool_calls", + "metadata", + "min_p", + "n", + "parallel_tool_calls", + "presence_penalty", + "previous_response_id", + "prompt_cache_key", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "safety_identifier", + "seed", + "service_tier", + "stop", + "stop_sequences", + "store", + "structured_outputs", + "temperature", + "thinking", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p", + "truncation", + "user" + ], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.14", + "completion": "0.28", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0.03" + }, + "top_provider": { + "context_length": 1048576, + "max_completion_tokens": 1048576, + "is_moderated": false + } + }, + { + "id": "example/embedding-model", + "name": "example/embedding-model", + "created": 0, + "description": "Fixture-only public row with a non-text modality; discovery must exclude it.", + "icon": "", + "owned_by": "fixture", + "is_public": true, + "type": "embedding", + "context_length": 8192, + "quantization": "bf16", + "architecture": { + "modality": "embedding", + "tokenizer": "transformers", + "instruct_type": "", + "parameter_count": 0 + }, + "tags": [ + "feature extraction" + ], + "supported_parameters": [], + "is_billed_by_token": true, + "pricing": { + "prompt": "0.01", + "completion": "0", + "image": "0", + "request": "0", + "video": "0", + "input_cache_reads": "0" + }, + "top_provider": { + "context_length": 8192, + "max_completion_tokens": 0, + "is_moderated": false + } + } + ] +} diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index 3e8ebbde5c..03b9e946e6 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -387,6 +387,7 @@ "cost-scoring.test.ts": "usage", "crash-guard.test.ts": "service", "credential-redirect-guard.test.ts": "lib", + "crusoe-provider.test.ts": "providers", "cursor-adapter.test.ts": "providers/cursor", "cursor-continuity-retention.test.ts": "providers/cursor", "cursor-arg-normalize.test.ts": "providers/cursor", diff --git a/tests/providers/crusoe-provider.test.ts b/tests/providers/crusoe-provider.test.ts new file mode 100644 index 0000000000..fedd6acb4b --- /dev/null +++ b/tests/providers/crusoe-provider.test.ts @@ -0,0 +1,286 @@ +import { afterEach, describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +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 { resolveProviderModelDiscovery } from "../../src/providers/model-discovery"; +import { PROVIDER_REGISTRY } 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"; +import { fixturePath } from "../helpers/repo-root"; + +const CRUSOE_FIXTURE = readFileSync(fixturePath("crusoe-models.json"), "utf8"); +const BASE_URL = "https://api.inference.crusoecloud.com/v1"; +const MODELS_URL = `${BASE_URL}/models`; +const TEST_KEY = "crusoe-test-key"; +const EFFORT_MODEL = "openai/gpt-oss-120b"; +const TOGGLE_MODEL = "zai-org/GLM-5.3"; +const VISION_MODEL = "moonshotai/Kimi-K2.6"; +const IMAGE_INPUT_MODELS = [ + "google/gemma-4-31b-it", + "moonshotai/Kimi-K2.6", + "nvidia/Nemotron-3-Nano-Omni-Reasoning-30B-A3B", + "yutori/n2", + "zai-org/GLM-5.3-Flash", +]; +// Every public serverless row in the 2026-09-12 capture; the two `example/` rows in the fixture +// (a private deployment and an embedding model) must be filtered out. +const PUBLIC_CHAT_IDS = [ + "Qwen/Qwen3-235B-A22B-Instruct-2507", + "deepseek-ai/DeepSeek-V3-0324", + "deepseek-ai/DeepSeek-V4-Pro", + "deepseek-ai/Deepseek-V4-Flash", + "google/gemma-4-31b-it", + "meta-llama/Llama-3.3-70B-Instruct", + "moonshotai/Kimi-K2.6", + "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B", + "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B", + "nvidia/Nemotron-3-Nano-Omni-Reasoning-30B-A3B", + "nvidia/Nemotron-3.5-Lightning-30B-A3B", + "openai/gpt-oss-120b", + "yutori/n2", + "zai-org/GLM-5.3", + "zai-org/GLM-5.3-Flash", + "zai/GLM-5.1", + "zai/GLM-5.2", +]; +const originalFetch = globalThis.fetch; + +afterEach(() => { + globalThis.fetch = originalFetch; + clearModelCache("crusoe"); +}); + +function registryEntry() { + const entry = PROVIDER_REGISTRY.find(row => row.id === "crusoe"); + if (!entry) throw new Error("missing crusoe registry entry"); + return entry; +} + +function providerConfig(overrides: Partial = {}): OcxConfig { + return { + port: 10100, + defaultProvider: "crusoe", + providers: { + crusoe: { + adapter: "openai-chat", + baseUrl: BASE_URL, + authMode: "key", + apiKey: TEST_KEY, + liveModels: true, + ...overrides, + }, + }, + }; +} + +function fixtureFetch(expectedRedirect: RequestRedirect) { + return (async (input: RequestInfo | URL, init?: RequestInit) => { + expect(String(input)).toBe(MODELS_URL); + expect(new Headers(init?.headers).get("authorization")).toBe(`Bearer ${TEST_KEY}`); + expect(init?.redirect).toBe(expectedRedirect); + return new Response(CRUSOE_FIXTURE, { + status: 200, + headers: { "content-type": "application/json" }, + }); + }) as typeof fetch; +} + +function chatRequest(config: OcxConfig, modelId: string, reasoning: string) { + const route = routeModel(config, `crusoe/${modelId}`); + const request = createOpenAIChatAdapter(route.provider).buildRequest({ + modelId: route.modelId, + context: { + messages: [{ role: "user", content: "ping", timestamp: 0 }], + tools: [{ + name: "ping", + description: "Return pong", + parameters: { type: "object", properties: {} }, + }], + }, + stream: true, + options: { reasoning }, + }); + return { request, body: JSON.parse(String(request.body)) as Record }; +} + +describe("Crusoe provider", () => { + test("registers a fixed Serverless Inference transport with public text-output discovery", () => { + expect(registryEntry()).toMatchObject({ + id: "crusoe", + label: "Crusoe", + adapter: "openai-chat", + baseUrl: BASE_URL, + authKind: "key", + dashboardUrl: "https://console.crusoecloud.com", + liveModels: true, + preserveCustomDestination: true, + parallelToolCalls: false, + reasoningEfforts: [], + modelReasoningEfforts: { [EFFORT_MODEL]: ["low", "medium", "high"] }, + directReasoningEffortModels: [EFFORT_MODEL], + modelInputModalities: Object.fromEntries(IMAGE_INPUT_MODELS.map(id => [id, ["text", "image"]])), + modelDiscovery: { + path: "models", + maxResponseBytes: 262_144, + maxModels: 256, + filter: { + allOf: [ + { path: ["is_public"], equalsAny: [true] }, + { path: ["architecture", "modality"], equalsAny: ["text", "multimodal"] }, + ], + }, + }, + }); + expect(registryEntry()).not.toHaveProperty("apiKeyValidation"); + expect(registryEntry().note).toContain("Public Serverless Inference"); + }); + + test("derives CLI and dashboard presets without persisting registry trust policy", () => { + const entry = registryEntry(); + expect(buildInitProviders()).toEqual(deriveInitProviders()); + expect(KEY_LOGIN_PROVIDERS.crusoe).toMatchObject({ + adapter: "openai-chat", + baseUrl: BASE_URL, + dashboardUrl: entry.dashboardUrl, + liveModels: true, + reasoningEfforts: [], + }); + expect(KEY_LOGIN_PROVIDERS.crusoe).not.toHaveProperty("apiKeyValidation"); + expect(buildInitProviders().find(row => row.id === "crusoe")).toMatchObject({ + kind: "key", + adapter: "openai-chat", + baseUrl: BASE_URL, + }); + expect(deriveProviderPresets().find(row => row.id === "crusoe")).toMatchObject({ + auth: "key", + dashboardUrl: entry.dashboardUrl, + }); + + const seed = providerConfigSeed(entry); + expect(seed).toMatchObject({ + adapter: "openai-chat", + baseUrl: BASE_URL, + authMode: "key", + liveModels: true, + parallelToolCalls: false, + reasoningEfforts: [], + modelReasoningEfforts: { [EFFORT_MODEL]: ["low", "medium", "high"] }, + }); + expect(seed).not.toHaveProperty("modelDiscovery"); + expect(seed).not.toHaveProperty("preserveCustomDestination"); + expect(seed).not.toHaveProperty("directReasoningEffortModels"); + expect(KEY_LOGIN_PROVIDERS.crusoe).not.toHaveProperty("modelDiscovery"); + expect(KEY_LOGIN_PROVIDERS.crusoe).not.toHaveProperty("preserveCustomDestination"); + }); + + test("validates a key through the Bearer-authenticated model list", async () => { + expect(buildModelsRequest(providerConfig().providers.crusoe!, TEST_KEY, "crusoe")).toEqual({ + url: MODELS_URL, + headers: { Authorization: `Bearer ${TEST_KEY}` }, + }); + + globalThis.fetch = fixtureFetch("error"); + expect(await validateApiKey("crusoe", KEY_LOGIN_PROVIDERS.crusoe!, TEST_KEY)).toBe(true); + + globalThis.fetch = (async () => new Response(JSON.stringify({ errors: ["Authentication failed"] }), { + status: 401, + headers: { "content-type": "application/json" }, + })) as typeof fetch; + expect(await validateApiKey("crusoe", KEY_LOGIN_PROVIDERS.crusoe!, "wrong-key")).toBe(false); + }); + + test("keeps public text-output rows, drops private and embedding rows, preserves ids and metadata", async () => { + globalThis.fetch = fixtureFetch("manual"); + + const config = withStubbedProviderFetch(providerConfig()); + const models = (await gatherRoutedModels(config)).filter(row => row.provider === "crusoe"); + const ids = models.map(row => row.id); + expect([...ids].sort()).toEqual([...PUBLIC_CHAT_IDS].sort()); + expect(ids).not.toContain("example/private-deployment"); + expect(ids).not.toContain("example/embedding-model"); + + const effortModel = models.find(row => row.id === EFFORT_MODEL); + expect(effortModel).toMatchObject({ + owned_by: "openai", + contextWindow: 131_072, + pricingStatus: "paid", + reasoningEfforts: ["low", "medium", "high"], + }); + expect(effortModel).not.toHaveProperty("inputModalities"); + expect(models.find(row => row.id === TOGGLE_MODEL)).toMatchObject({ + owned_by: "zai-org", + contextWindow: 1_048_576, + reasoningEfforts: [], + }); + expect(models.find(row => row.id === VISION_MODEL)).toMatchObject({ + owned_by: "moonshotai", + contextWindow: 262_144, + inputModalities: ["text", "image"], + }); + expect(models.find(row => row.id === "nvidia/Nemotron-3.5-Lightning-30B-A3B")).toMatchObject({ + contextWindow: 262_144, + }); + + for (const modelId of ids) { + expect(routeModel(config, `crusoe/${modelId}`).modelId).toBe(modelId); + expect(routeModel(config, routedSlug("crusoe", modelId)).modelId).toBe(modelId); + } + }); + + test("sends reasoning_effort only to gpt-oss-120b and never advertises parallel tool calls", () => { + const config = providerConfig(); + + const effort = chatRequest(config, EFFORT_MODEL, "high"); + expect(effort.request.url).toBe(`${BASE_URL}/chat/completions`); + expect(effort.request.headers.Authorization).toBe(`Bearer ${TEST_KEY}`); + expect(effort.body.model).toBe(EFFORT_MODEL); + expect(effort.body.reasoning_effort).toBe("high"); + expect(effort.body).not.toHaveProperty("parallel_tool_calls"); + + const toggle = chatRequest(config, TOGGLE_MODEL, "high"); + expect(toggle.body.model).toBe(TOGGLE_MODEL); + expect(toggle.body).not.toHaveProperty("reasoning_effort"); + expect(toggle.body).not.toHaveProperty("parallel_tool_calls"); + }); + + test("does not retarget an older same-named custom provider or adapter", () => { + const customConfig = providerConfig({ baseUrl: "https://custom.example/v1" }); + const route = routeModel(customConfig, "crusoe/custom-model"); + expect(route.provider).toMatchObject({ + adapter: "openai-chat", + baseUrl: "https://custom.example/v1", + authMode: "key", + }); + expect(resolveProviderModelDiscovery("crusoe", customConfig.providers.crusoe!).spec).toBeUndefined(); + expect(buildModelsRequest(customConfig.providers.crusoe!, "custom-key", "crusoe")).toEqual({ + url: "https://custom.example/v1/models", + headers: { Authorization: "Bearer custom-key" }, + }); + + const nearMissConfig = providerConfig({ baseUrl: "https://api.inference.crusoecloud.com/v2" }); + expect( + resolveProviderModelDiscovery("crusoe", nearMissConfig.providers.crusoe!).spec, + ).toBeUndefined(); + + const customAdapter = routeModel(providerConfig({ + adapter: "anthropic", + baseUrl: "https://custom.example/anthropic", + }), "crusoe/custom-model"); + expect(customAdapter.provider).toMatchObject({ + adapter: "anthropic", + baseUrl: "https://custom.example/anthropic", + authMode: "key", + }); + }); +}); diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index d33d10f4c5..078e560086 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -33,7 +33,7 @@ function nativeTemplate(): Record { const EXPECTED_KEY_PROVIDER_IDS = [ "anthropic-apikey", "openai-apikey", "meta-model", "umans", "opencode-go", "neuralwatt", "openrouter", "cline-pass", "cline", "orcarouter", "packycode", "bizrouter", "groq", "google", "google-vertex", "azure-openai", - "deepseek", "cerebras", "chutes", "deepinfra", "hyperbolic", "nscale", "vultr", "baseten", "commandcode", "sambanova", "nebius", "digitalocean", "scaleway", "featherless", "novita", "together", "fireworks", "firepass", "moonshot", + "deepseek", "cerebras", "chutes", "deepinfra", "hyperbolic", "nscale", "vultr", "baseten", "commandcode", "sambanova", "nebius", "crusoe", "digitalocean", "scaleway", "featherless", "novita", "together", "fireworks", "firepass", "moonshot", "huggingface", "nvidia", "venice", "zai", "zhipu-bigmodel", "zhipu-bigmodel-coding", "zhipu-bigmodel-responses", "nanogpt", "synthetic", "siliconflow", "qwen-cloud", "tencent-coding-plan", "volcengine", "volcengine-coding-plan", "volcengine-agent-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",