diff --git a/bun.lock b/bun.lock index 329084661e..6f21e45a6b 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,9 @@ "@types/bun": "1.4.0", "typescript": "7.0.2", }, + "optionalDependencies": { + "wreq-js": "2.3.1", + }, }, }, "trustedDependencies": [ @@ -309,6 +312,8 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "wreq-js": ["wreq-js@2.3.1", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ] }, "sha512-vaKasaKeskrDKEuuO5Q5uamEG9a6FrF5ZSicH7TCvYS4RxF7/gzaU/vYqwJzcs+uydyJPVWY1KCvfVCgp0tiGA=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index feedf5ad0c..3b7f9b99d7 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -66,7 +66,8 @@ sauvegarde dont le contenu diffère, puis réécrit en identifiants sans préfix | --- | --- | --- | | `adapter` | `string` | L'un des `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai` (ou alias `azure`). | | `baseUrl` | `string` | URL de base de l'API en amont. La plupart des points de terminaison fixes intégrés ignorent une valeur incompatible ; les préréglages de clés protégés contre les collisions préservent une ancienne destination personnalisée portant le même nom. | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | Cadencement facultatif du démarrage des requêtes sortantes côté client, distinct de l’utilisation, de la facturation et des indicateurs de limitation en amont. Le nombre de requêtes par minute est converti en intervalle régulier ; `minIntervalMs` peut imposer un intervalle plus long. Les limites du fournisseur s’appliquent à tous ses modèles, tandis que les entrées `models` ciblent les identifiants exacts des modèles en amont, par exemple `nvidia/llama-3.1-nemotron-ultra-253b-v1`, et ne peuvent qu’ajouter du délai. L’attente dans la file ne consomme pas le délai d’expiration des en-têtes de réponse en amont. Les requêtes HTTP, Responses WebSocket et les distributions explicites `fetchResponse`/`runTurn` des adaptateurs sont couvertes. | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | Cadencement facultatif du démarrage des requêtes sortantes côté client ; `jitterMs` ajoute uniquement un délai aléatoire positif (0 à 60 000 ms). Les règles de modèle ne peuvent qu’augmenter le délai. | +| `tlsProfile?` | `"antigravity-browser"` | Profil TLS/HTTP2 expérimental et non officiel, limité à Google Antigravity Cloud Code Assist et à ses hôtes canoniques. Il ne garantit ni la conformité aux conditions d’utilisation ni l’absence de suspension, peut rendre le trafic plus distinctif et revient à Bun si l’initialisation échoue. | | `responsesPath?` | `string` | Chemin de ressource relatif pour les requêtes d'authentification par clé `openai-responses`. Il doit commencer par `/` et ne contenir aucun schéma, requête ou fragment. | | `supportsServiceTier?` | `boolean` | Repli à trois états pour la capacité `service_tier`. `true` : le mode rapide peut injecter le champ et les valeurs de l’appelant sont conservées. `false` : le champ est retiré et jamais injecté, et aucune déclaration précise de modèle ne peut le réactiver. Absent : le fournisseur n’est pas classé ; les valeurs de l’appelant sont conservées intactes et le mode rapide n’injecte rien, sauf pour un modèle exact activé. Le registre classe OpenAI canonique comme `true`, et DeepSeek ainsi que Volcengine Ark comme `false`. Ne le définissez explicitement que pour les passerelles personnalisées qui prennent réellement en charge les niveaux. Les routes Chat exigent en plus une autorisation globale ou propre au modèle. | | `modelSupportsServiceTier?` | `Record` | Remplacements de capacité par identifiant exact de modèle en amont. La valeur exacte `true` autorise ce modèle Chat même sans `chatServiceTier` ; `false` restreint les valeurs globales et l’autorisation Chat. Une valeur globale explicite `supportsServiceTier: false` reste fermée et ne peut pas être réactivée. Les modèles non déclarés suivent le comportement global. La requête de gestion `PATCH /api/providers` fusionne les entrées et accepte `null` pour en supprimer une. | diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index e58af8360c..89d9902c09 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -119,6 +119,21 @@ ocx logout | `cursor` | `cursor` | `https://api2.cursor.sh` | Experimental PKCE login, live HTTP/2 transport with an opt-in HTTP/1.1 compatibility path, and account-filtered model discovery. | | `github-copilot` | `openai-chat` | `https://api.githubcopilot.com` | Experimental. GitHub device flow + `copilot_internal` exchange (VS Code OAuth client). Requires an active Copilot subscription; not an official third-party API. | +### Antigravity pacing and TLS profile + +The built-in `google-antigravity` provider uses conservative request pacing by default: 30 RPM, +at least 2,000 ms between request starts, and up to 500 ms of positive jitter. Existing explicit +`requestPacing` settings remain authoritative; `jitterMs` may be set from 0 through 60,000 ms and +only delays a start. Model rules can make the provider slower, never faster. + +The dashboard can explicitly enable `tlsProfile: "antigravity-browser"` for this provider. This is +an experimental, unofficial compatibility mechanism, not a compliance feature. +It may make traffic more distinctive, and initialization failures fall back to Bun; requests that +already reached the native transport are not replayed. The profile is limited to canonical Cloud +Code Assist hosts, keeps certificate and hostname verification enabled, and leaves OAuth/token/ +onboarding requests on standard Bun TLS. Users who prioritize account-policy safety should use the +official Gemini API-key, Vertex, or documented Gemini Code Assist routes. + After a terminal Nous refresh failure, run `ocx login nous` to reauthenticate. For the canonical Kimi Coding Plan presets (`kimi` account login and `kimi-code` API key), diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index 27cf1406ec..6bee512f22 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -56,7 +56,8 @@ account を削除しても mapping は保持され、同じ id を再追加す | --- | --- | --- | | `adapter` | `string` | `openai-chat`、`openai-responses`、`anthropic`、`google`、`kiro`、`cursor`、`azure-openai` (または別名 `azure`) のいずれか。 | | `baseUrl` | `string` |アップストリーム API のベース URL。ほとんどの組み込み固定エンドポイントは不一致を無視します。衝突安全キー プリセットは、古い同じ名前のカスタム宛先を保持します。 | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | 上流の使用量、請求、レート制限表示とは別の、クライアント側の送信開始間隔調整です。プロバイダー制限は全モデルに適用され、`models` は上流の正確なモデル ID に一致し、遅延を増やす場合のみ有効です。キュー待機は応答ヘッダーのタイムアウトを消費しません。HTTP、Responses WebSocket、明示的なアダプターの `fetchResponse`/`runTurn` 送信を対象にします。 | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | 上流の使用量などとは別の送信間隔調整です。`jitterMs` は 0~60,000 ms の正のランダム遅延だけを追加し、モデル設定は遅延を増やす場合のみ有効です。 | +| `tlsProfile?` | `"antigravity-browser"` | Google Antigravity Cloud Code Assist の正規ホストだけで使える、実験的で非公式な TLS/HTTP2 互換プロファイルです。利用規約への準拠や停止防止を保証せず、トラフィックを特徴的にする可能性があり、初期化に失敗すると Bun に戻ります。 | | `responsesPath?` | `string` |キー認証 `openai-responses` リクエストの相対リソース パス。 `/` で始まり、スキーム、クエリ、またはフラグメントが含まれていない必要があります。 | | `supportsServiceTier?` | `boolean` | `service_tier` ケイパビリティの 3 状態です。`true`: fast モードが注入でき、呼び出し元の値も保持されます。`false`: フィールドは削除され、注入もされません (非対応と文書化されたアップストリームには送りません)。未設定: 未分類 — 呼び出し元の値はそのまま保持され、fast モードは注入しません。レジストリは正規 OpenAI (`true`)、DeepSeek、Volcengine Ark (`false`) を分類します。実際にティアをサポートするカスタム ゲートウェイにのみ明示的に設定してください。 | | `preserveResponsesReasoningContent?` | `boolean` | リプレイされる Responses reasoning アイテムの平文 reasoning コンテンツを消去せずに保持します (消去は ChatGPT バックエンドのルールです)。DeepSeek のように reasoning リプレイを受け入れるアップストリームで有効にしてください。プロキシ生成の `ocxr1` エンベロープは常に削除されます。 | diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index 707129b2ed..4465154f89 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -56,7 +56,8 @@ managed map을 활성화하면 privacy-safe selector를 만들고, 이후 계정 | --- | --- | --- | | `adapter` | `string` | `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai` 중 하나이며, `azure`는 별칭입니다. | | `baseUrl` | `string` | 상위 API 기본 URL입니다. 대부분의 내장 고정 엔드포인트는 불일치를 무시합니다. 충돌 안전 키 프리셋은 같은 이름의 이전 사용자 지정 목적지를 보존합니다. | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | 업스트림 사용량, 과금, rate-limit 지표와 별개인 선택적 클라이언트 측 아웃바운드 요청 시작 속도 조절입니다. Provider 제한은 모든 모델에 적용되고 `models` 항목은 정확한 업스트림 모델 ID와 일치하며 지연을 더 늘릴 때만 적용됩니다. 큐 대기는 응답 헤더 타임아웃을 소모하지 않습니다. HTTP, Responses WebSocket, 명시적 어댑터 `fetchResponse`/`runTurn` 전송을 포함합니다. | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | 업스트림 지표와 별개인 요청 시작 조절입니다. `jitterMs`는 0~60,000ms의 양의 무작위 지연만 추가하며 모델 규칙은 지연을 늘릴 때만 적용됩니다. | +| `tlsProfile?` | `"antigravity-browser"` | Google Antigravity Cloud Code Assist 정규 호스트에만 적용되는 실험적·비공식 TLS/HTTP2 호환 프로필입니다. 약관 준수나 정지 방지를 보장하지 않고 트래픽을 더 식별하기 쉽게 만들 수 있으며 초기화 실패 시 Bun으로 대체됩니다. | | `responsesPath?` | `string` | 키 인증 `openai-responses` 요청의 상대 리소스 경로입니다. 반드시 `/`로 시작해야 하며 스킴, query, fragment를 포함하면 안 됩니다. | | `supportsServiceTier?` | `boolean` | `service_tier` 케이퍼빌리티 3상태입니다. `true`: fast 모드가 주입할 수 있고 호출자 값도 보존합니다. `false`: 필드를 제거하고 절대 주입하지 않습니다(미지원으로 문서화된 업스트림에는 볼 수 없습니다). 미설정: 미분류 — 호출자가 준 값은 그대로 보존하고 fast 모드는 주입하지 않습니다. 레지스트리는 정식 OpenAI(`true`), DeepSeek, Volcengine Ark(`false`)를 분류하며, 실제로 티어를 지원하는 커스텀 게이트웨이에만 명시적으로 설정하세요. | | `preserveResponsesReasoningContent?` | `boolean` | 리플레이되는 Responses reasoning 항목의 평문 reasoning 내용을 지우지 않고 유지합니다(지우는 것은 ChatGPT 백엔드 규칙입니다). DeepSeek처럼 reasoning 리플레이를 허용하는 업스트림에 켜세요. 프록시가 만든 `ocxr1` 봉투는 항상 제거됩니다. | diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index c44b628714..a1c70674a9 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -66,7 +66,8 @@ differing backup and rewrites known legacy namespaced selected ids to bare ids. | --- | --- | --- | | `adapter` | `string` | One of `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai` (or alias `azure`). | | `baseUrl` | `string` | Upstream API base URL. Most built-in fixed endpoints ignore a mismatch; collision-safe key presets preserve an older same-named custom destination. | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | Optional client-side outbound request-start pacing, separate from upstream usage, billing, and rate-limit indicators. RPM is converted to an even interval; `minIntervalMs` may impose a longer interval. Provider limits apply across all models, while `models` entries use exact upstream model IDs (for example `nvidia/llama-3.1-nemotron-ultra-253b-v1`) and can only add delay. Queue waits do not consume the upstream response-header timeout. HTTP, Responses WebSocket, and explicit adapter `fetchResponse`/`runTurn` dispatches are covered. | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | Optional client-side outbound request-start pacing, separate from upstream usage, billing, and rate-limit indicators. RPM is converted to an even interval; `minIntervalMs` may impose a longer interval; `jitterMs` adds only a positive random delay (0–60,000 ms). Provider limits apply across all models, while `models` entries use exact upstream model IDs and can only add delay. Queue waits do not consume the upstream response-header timeout. HTTP, Responses WebSocket, and explicit adapter `fetchResponse`/`runTurn` dispatches are covered. | +| `tlsProfile?` | `"antigravity-browser"` | Explicit, experimental Antigravity-only TLS/HTTP2 compatibility profile. It requires Google OAuth Cloud Code Assist and canonical Antigravity hosts. It is unofficial, does not ensure Terms-of-Service compliance or prevent suspension, may make traffic more distinctive, and falls back to Bun if initialization fails. OAuth/token/onboarding traffic remains on standard Bun TLS. Prefer official Gemini API-key, Vertex, or documented Code Assist routes when policy safety matters. | | `upstreamHttpVersion?` | `"auto" \| "http1.1" \| "h1" \| "http2" \| "h2"` | Pin the HTTP version used for upstream requests to this provider. Defaults to `auto`, which lets Bun negotiate. An explicit pin requires an HTTPS target and fails locally when it cannot be honored. Set `http1.1` when a provider's HTTP/2 SSE stream stalls instead of delivering events — the symptom is a long-running streaming request that produces nothing and eventually times out. For Cursor, `http1.1`/`h1` selects its `RunSSE` + `BidiAppend` compatibility transport for inference and also pins live model discovery. Management `POST`/`PATCH` accept `null` to clear it back to `auto`. | | `responsesPath?` | `string` | Relative resource path for key-auth `openai-responses` requests. It must start with `/` and contain no scheme, query, or fragment. | | `supportsServiceTier?` | `boolean` | Tri-state canonical Fast capability fallback. `true` publishes Fast in the catalog, satisfies service-tier routing requirements, contributes a supported fingerprint, and lets fast mode inject the provider's canonical wire value on a compatible final adapter. `false` strips the field and never injects, and exact model declarations cannot reopen it. Absent leaves the provider unclassified: fast mode does not inject or normalize a canonical caller value, and caller values obey the final wire's forwarding permission (`chatServiceTier` on Chat; passthrough on Responses). The registry classifies canonical OpenAI (`true`), DeepSeek, and Volcengine Ark (`false`); set it explicitly only for custom gateways that genuinely support tiers. | @@ -209,7 +210,10 @@ configured under [`claudeCode.authMode`](/reference/configuration/server/#claude Dashboard connection tests and live model discovery use a bounded GET-only transport. Without an outbound proxy, opencodex resolves the hostname once and connects only to that validated address. HTTPS retains the original Host, SNI, and certificate verification; provider config cannot disable -certificate checks. +certificate checks. The native TLS profile performs a direct DNS safety preflight, but its native +transport cannot pin the checked address, so a small DNS time-of-check/time-of-use residual remains; +normal certificate and hostname verification are separate controls. Proxy-routed requests retain +the proxy's peer-selection boundary. When `HTTP_PROXY`, `HTTPS_PROXY`, or `ALL_PROXY` applies, these operations keep Bun's native fetch. URL and literal-address checks still run, but the proxy chooses the final route, DNS answer, and peer, diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index 21b70bebb5..20a662aea7 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -69,7 +69,8 @@ cross-route credential fallback не существует. Строки API GPT- | --- | --- | --- | | `adapter` | `string` | Один из `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai` (или alias `azure`). | | `baseUrl` | `string` | Базовый URL API upstream'а. Большинство built-in fixed-endpoint'ов игнорируют несовпадение; collision-safe key-preset'ы сохраняют старый custom destination с тем же именем. | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | Опциональное клиентское выравнивание начала исходящих запросов, отдельное от учёта использования, биллинга и индикаторов rate limit апстрима. Лимит провайдера действует на все модели, а `models` сопоставляется с точными ID моделей апстрима и может только увеличить задержку. Ожидание очереди не расходует таймаут заголовков ответа. Поддерживаются HTTP, Responses WebSocket и явные вызовы адаптеров `fetchResponse`/`runTurn`. | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | Клиентское выравнивание начала запросов. `jitterMs` добавляет только положительную случайную задержку от 0 до 60 000 мс; правила моделей могут лишь увеличить задержку. | +| `tlsProfile?` | `"antigravity-browser"` | Экспериментальный неофициальный TLS/HTTP2-профиль только для Google Antigravity Cloud Code Assist и канонических хостов. Он не гарантирует соблюдение условий или отсутствие блокировки, может сделать трафик более отличимым и переходит на Bun при ошибке инициализации. | | `responsesPath?` | `string` | Relative resource path для key-auth запросов `openai-responses`. Должен начинаться с `/` и не может содержать scheme, query или fragment. | | `supportsServiceTier?` | `boolean` | Три состояния поддержки `service_tier`. `true`: fast mode может подставлять поле, значения вызывающего сохраняются. `false`: поле удаляется и никогда не подставляется (апстрим, для которого задокументировано отсутствие поддержки, не должен его получать). Не задано: провайдер не классифицирован — значения вызывающего сохраняются без изменений, fast mode не подставляет. Registry классифицирует canonical OpenAI (`true`), DeepSeek и Volcengine Ark (`false`); задавайте явно только для custom gateway'ев, реально поддерживающих tier'ы. | | `preserveResponsesReasoningContent?` | `boolean` | Сохранять plaintext reasoning content в replay'нутых Responses reasoning item'ах вместо очистки (очистка — правило ChatGPT backend'а). Включайте для upstream'ов, чей контракт принимает reasoning replay, например DeepSeek. Proxy-minted `ocxr1` envelope'ы удаляются всегда. | diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index d4e414700a..344c8615ed 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -76,6 +76,8 @@ alanlı seçilmiş kimlikleri yalın kimliklere yeniden yazar. | --- | --- | --- | | `adapter` | `string` | `openai-chat`, `openai-responses`, `anthropic`, `google`, `kiro`, `cursor`, `azure-openai` (veya takma ad `azure`) seçeneklerinden biri. | | `baseUrl` | `string` | Yukarı akış API temel URL'si. Çoğu yerleşik sabit uç nokta uyumsuzluğu yok sayar; çakışma güvenli anahtar önayarları aynı adlı daha eski özel bir hedefi korur. | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | İstemci tarafı istek başlangıç aralığı. `jitterMs` yalnızca 0–60.000 ms pozitif rastgele gecikme ekler; model kuralları yalnızca gecikmeyi artırabilir. | +| `tlsProfile?` | `"antigravity-browser"` | Yalnızca Google Antigravity Cloud Code Assist kanonik ana bilgisayarları için deneysel, resmi olmayan TLS/HTTP2 uyumluluk profili. Kullanım şartlarına uyumu veya askıya alınmayı önlemeyi garanti etmez, trafiği daha ayırt edilebilir kılabilir ve başlatma başarısız olursa Bun'a döner. | | `responsesPath?` | `string` | Anahtar kimlik doğrulamalı `openai-responses` istekleri için göreli kaynak yolu. `/` ile başlamalı ve şema, sorgu veya parça içermemelidir. | | `supportsServiceTier?` | `boolean` | Üç durumlu `service_tier` yeteneği. `true`: hızlı mod enjekte edebilir ve arayan değerleri korunur. `false`: alan kaldırılır ve asla enjekte edilmez (desteklemediği belgelenen yukarı akış bunu almamalıdır). Yok: sağlayıcı sınıflandırılmamıştır — arayan tarafından sağlanan değerler dokunulmadan korunur ve hızlı mod asla enjekte etmez. Kayıt defteri kurallı OpenAI'yi (`true`), DeepSeek'i ve Volcengine Ark'ı (`false`) sınıflandırır; bunu yalnızca katmanları gerçekten destekleyen özel ağ geçitleri için açıkça ayarlayın. | | `preserveResponsesReasoningContent?` | `boolean` | Düz metin akıl yürütme içeriğini boşaltmak yerine (boşaltma ChatGPT arka ucunun kuralıdır) tekrarlanan Responses akıl yürütme öğelerinde tutun. DeepSeek gibi sözleşmesi akıl yürütme tekrarını kabul eden yukarı akışlar için etkinleştirin. Proxy tarafından basılan `ocxr1` zarfları her zaman kaldırılır. | @@ -479,4 +481,3 @@ bildirir; senkronize edilen katalog `xhigh`'ı ayrı tutarken `max` bildirir. "visionSidecar": { "enabled": true } } ``` - diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index 1564842cbb..64a8f9a3c3 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -56,7 +56,8 @@ selector,而不是分配一个新名称。 | --- | --- | --- | | `adapter` | `string` | `openai-chat`、`openai-responses`、`anthropic`、`google`、`kiro`、`cursor`、`azure-openai`(或别名 `azure`)之一。 | | `baseUrl` | `string` | 上游 API 基础 URL。大多数内置固定端点会忽略不匹配的值;具备冲突安全键的预设会保留一个更早、同名的自定义目标。 | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | 可选的客户端出站请求启动节流,与上游用量、计费和限流指标相互独立。提供商限制适用于所有模型,`models` 按上游模型精确 ID 匹配且只能增加延迟。排队等待不计入响应头超时。覆盖 HTTP、Responses WebSocket 以及显式适配器 `fetchResponse`/`runTurn` 调用。 | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | 可选的客户端请求启动节流。`jitterMs` 只会增加 0 到 60,000 毫秒的正随机延迟;模型规则只能进一步增加延迟。 | +| `tlsProfile?` | `"antigravity-browser"` | 仅用于 Google Antigravity Cloud Code Assist 正规主机的实验性、非官方 TLS/HTTP2 兼容配置。不保证遵守服务条款或避免停用,可能使流量更具特征,初始化失败时回退到 Bun。 | | `responsesPath?` | `string` | 用于 key-auth `openai-responses` 请求的相对资源路径。必须以 `/` 开头,且不能包含 scheme、query 或 fragment。 | | `supportsServiceTier?` | `boolean` | `service_tier` 能力的三态。`true`:fast 模式可以注入,调用方提供的值也会被保留。`false`:剥离该字段且绝不注入(已明确不支持的上游不会收到它)。未设置:未分类——调用方提供的值原样保留,fast 模式绝不注入。注册表已对官方 OpenAI(`true`)、DeepSeek 和 Volcengine Ark(`false`)分类;仅对真正支持分层的自定义网关显式设置。 | | `preserveResponsesReasoningContent?` | `boolean` | 在重放的 Responses reasoning 项中保留明文 reasoning 内容,而不是清空(清空是 ChatGPT 后端的规则)。对接受 reasoning 重放的上游(如 DeepSeek)启用。代理生成的 `ocxr1` 信封始终会被剥离。 | diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index f47b5bef05..146b856c7b 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -40,7 +40,8 @@ description: 供應商項目、認證、端點、模型目錄、配額、context | --- | --- | --- | | `adapter` | `string` | `openai-chat`、`openai-responses`、`anthropic`、`google`、`kiro`、`cursor`、`azure-openai`(或別名 `azure`)之一。 | | `baseUrl` | `string` | 上游 API base URL。多數內建固定端點忽略不符;碰撞安全的金鑰預設保留較舊的同名自訂目的地。 | -| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, models? }` | 選用的用戶端出站請求啟動節流,與上游用量、計費及限流指標彼此獨立。供應商限制適用於所有模型,`models` 依上游模型精確 ID 比對且只能增加延遲。排隊等待不計入回應標頭逾時。涵蓋 HTTP、Responses WebSocket 及明確的適配器 `fetchResponse`/`runTurn` 呼叫。 | +| `requestPacing?` | `{ enabled, requestsPerMinute?, minIntervalMs?, jitterMs?, models? }` | 可選的用戶端請求啟動節流。`jitterMs` 只會增加 0 至 60,000 毫秒的正隨機延遲;模型規則只能進一步增加延遲。 | +| `tlsProfile?` | `"antigravity-browser"` | 僅適用於 Google Antigravity Cloud Code Assist 正規主機的實驗性、非官方 TLS/HTTP2 相容設定。不保證符合服務條款或避免停權,可能讓流量更具辨識度,初始化失敗時回退到 Bun。 | | `responsesPath?` | `string` | Key-auth `openai-responses` 請求的相對資源路徑。必須以 `/` 開頭且不含 scheme、query 或 fragment。 | | `disabled?` | `boolean` | 將供應商保留在磁碟上但排除於路由與模型/目錄清單。 | | `apiKey?` | `string` | API 金鑰,或在請求時解析的 `${ENV_VAR}` / `$ENV_VAR` 參考。 | diff --git a/gui/src/components/provider-workspace/ProviderSettings.tsx b/gui/src/components/provider-workspace/ProviderSettings.tsx index 847fe7e712..b92e3a28ae 100644 --- a/gui/src/components/provider-workspace/ProviderSettings.tsx +++ b/gui/src/components/provider-workspace/ProviderSettings.tsx @@ -25,7 +25,7 @@ const ADAPTERS = ["openai-responses", "openai-chat", "anthropic", "google", "azu const EMPTY_MODELS: string[] = []; type ChoicesStatus = "idle" | "loading" | "ready" | "error"; -type PacingRule = { requestsPerMinute?: number; minIntervalMs?: number }; +type PacingRule = { requestsPerMinute?: number; minIntervalMs?: number; jitterMs?: number }; type PacingStatus = { enabled: boolean; queued: number; nextSlotInMs: number; lastStartedAt?: number; lastModelId?: string }; type CursorHttpVersion = "http2" | "http1.1"; @@ -44,14 +44,20 @@ function positiveInteger(value: string): number | undefined { const parsed = Number(value); return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined; } +function jitterInteger(value: string): number | undefined { + if (!value.trim()) return undefined; + const parsed = Number(value); + return Number.isSafeInteger(parsed) && parsed >= 0 && parsed <= 60_000 ? parsed : undefined; +} function pacingSignature(value: WorkspaceItem["requestPacing"] | undefined): string { const models = Object.entries(value?.models ?? {}) .sort(([left], [right]) => left.localeCompare(right)) - .map(([model, rule]) => [model, rule.requestsPerMinute ?? null, rule.minIntervalMs ?? null]); + .map(([model, rule]) => [model, rule.requestsPerMinute ?? null, rule.minIntervalMs ?? null, rule.jitterMs ?? null]); return JSON.stringify([ value?.enabled === true, value?.requestsPerMinute ?? null, value?.minIntervalMs ?? null, + value?.jitterMs ?? null, models, ]); } @@ -93,11 +99,14 @@ export default function ProviderSettings({ const [pacingEnabled, setPacingEnabled] = useState(item.requestPacing?.enabled === true); const [pacingRpm, setPacingRpm] = useState(() => numberDraft(item.requestPacing?.requestsPerMinute)); const [pacingDelay, setPacingDelay] = useState(() => numberDraft(item.requestPacing?.minIntervalMs)); + const [pacingJitter, setPacingJitter] = useState(() => numberDraft(item.requestPacing?.jitterMs)); const [pacingModels, setPacingModels] = useState>(() => ({ ...(item.requestPacing?.models ?? {}) })); const [pacingModelId, setPacingModelId] = useState(""); const [pacingModelRpm, setPacingModelRpm] = useState(""); const [pacingModelDelay, setPacingModelDelay] = useState(""); + const [pacingModelJitter, setPacingModelJitter] = useState(""); const [pacingStatus, setPacingStatus] = useState(null); + const [tlsProfile, setTlsProfile] = useState(item.tlsProfile); /* eslint-disable react-hooks/set-state-in-effect -- intentional form reset when saved provider fields change */ useEffect(() => { @@ -113,11 +122,13 @@ export default function ProviderSettings({ setPacingEnabled(item.requestPacing?.enabled === true); setPacingRpm(numberDraft(item.requestPacing?.requestsPerMinute)); setPacingDelay(numberDraft(item.requestPacing?.minIntervalMs)); + setPacingJitter(numberDraft(item.requestPacing?.jitterMs)); setPacingModels({ ...(item.requestPacing?.models ?? {}) }); + setTlsProfile(item.tlsProfile); setMsg(null); setModeMsg(null); queueMicrotask(() => setEndpointChoice(matchChoiceId(baseUrlChoices, item.baseUrl))); - }, [item.adapter, item.baseUrl, item.defaultModel, item.authMode, item.apiKeyTransport, item.keyOptional, item.note, item.allowPrivateNetwork, savedLiveModels, savedCursorHttpVersion, item.requestPacing, baseUrlChoices]); + }, [item.adapter, item.baseUrl, item.defaultModel, item.authMode, item.apiKeyTransport, item.keyOptional, item.note, item.allowPrivateNetwork, savedLiveModels, savedCursorHttpVersion, item.requestPacing, item.tlsProfile, baseUrlChoices]); /* eslint-enable react-hooks/set-state-in-effect */ // Account mode syncs on its own: a mode PATCH refresh must not reset an in-progress @@ -183,8 +194,9 @@ export default function ProviderSettings({ enabled: pacingEnabled, ...(positiveRpm(pacingRpm) !== undefined ? { requestsPerMinute: positiveRpm(pacingRpm) } : {}), ...(positiveInteger(pacingDelay) !== undefined ? { minIntervalMs: positiveInteger(pacingDelay) } : {}), + ...(jitterInteger(pacingJitter) !== undefined ? { jitterMs: jitterInteger(pacingJitter) } : {}), ...(Object.keys(pacingModels).length > 0 ? { models: pacingModels } : {}), - }), [pacingDelay, pacingEnabled, pacingModels, pacingRpm]); + }), [pacingDelay, pacingEnabled, pacingJitter, pacingModels, pacingRpm]); const dirty = adapter.trim() !== item.adapter || baseUrl.trim() !== item.baseUrl @@ -196,7 +208,8 @@ export default function ProviderSettings({ || liveModels !== savedLiveModels || (adapter.trim() === "cursor" && cursorHttpVersion !== savedCursorHttpVersion); const pacingDirty = pacingSignature(pacingDraft) !== pacingSignature(item.requestPacing); - const formDirty = dirty || pacingDirty; + const tlsDirty = tlsProfile !== item.tlsProfile; + const formDirty = dirty || pacingDirty || tlsDirty; useEffect(() => { onDirtyChange?.(formDirty); return () => onDirtyChange?.(false); }, [formDirty, onDirtyChange]); @@ -229,13 +242,21 @@ export default function ProviderSettings({ ? resolvedBaseUrlForChoice(baseUrlChoices, endpointChoice, baseUrl) : baseUrl.trim(); if (!adapter.trim() || !nextBaseUrl) { setMsg({ ok: false, text: t("pws.adapterBaseRequired") }); return false; } + if (tlsDirty && tlsProfile === "antigravity-browser" && !window.confirm(t("pws.antigravityTlsConfirm"))) { + setTlsProfile(item.tlsProfile); + return false; + } setSaving(true); setMsg(null); try { - if (pacingEnabled && !pacingDraft.requestsPerMinute && !pacingDraft.minIntervalMs && !pacingDraft.models) { + if (pacingEnabled + && pacingDraft.requestsPerMinute === undefined + && pacingDraft.minIntervalMs === undefined + && pacingDraft.jitterMs === undefined + && pacingDraft.models === undefined) { setMsg({ ok: false, text: t("pws.pacingRuleRequired") }); return false; } - const pacingOnly = pacingDirty && !dirty; + const pacingOnly = pacingDirty && !dirty && !tlsDirty; const patch: ProviderUpdatePatch = pacingOnly ? { requestPacing: pacingDraft } : { @@ -246,6 +267,7 @@ export default function ProviderSettings({ note: note.trim(), allowPrivateNetwork, ...(pacingDirty ? { requestPacing: pacingDraft } : {}), + ...(tlsDirty ? { tlsProfile: tlsProfile ?? null } : {}), }; if (!pacingOnly) { // Keep omitted legacy values omitted unless the user actually changes this toggle. @@ -303,6 +325,8 @@ export default function ProviderSettings({ setCursorHttpVersion(savedCursorHttpVersion); setMsg(null); setPacingEnabled(item.requestPacing?.enabled === true); setPacingRpm(numberDraft(item.requestPacing?.requestsPerMinute)); setPacingDelay(numberDraft(item.requestPacing?.minIntervalMs)); setPacingModels({ ...(item.requestPacing?.models ?? {}) }); + setPacingJitter(numberDraft(item.requestPacing?.jitterMs)); setPacingModelJitter(""); + setTlsProfile(item.tlsProfile); setEndpointChoice(matchChoiceId(baseUrlChoices, item.baseUrl)); }; @@ -319,9 +343,10 @@ export default function ProviderSettings({ const modelId = pacingModelId.trim(); const rpm = positiveRpm(pacingModelRpm); const delay = positiveInteger(pacingModelDelay); - if (!modelId || (rpm === undefined && delay === undefined)) return; - setPacingModels(current => ({ ...current, [modelId]: { ...(rpm !== undefined ? { requestsPerMinute: rpm } : {}), ...(delay !== undefined ? { minIntervalMs: delay } : {}) } })); - setPacingModelId(""); setPacingModelRpm(""); setPacingModelDelay(""); + const jitter = jitterInteger(pacingModelJitter); + if (!modelId || (rpm === undefined && delay === undefined && jitter === undefined)) return; + setPacingModels(current => ({ ...current, [modelId]: { ...(rpm !== undefined ? { requestsPerMinute: rpm } : {}), ...(delay !== undefined ? { minIntervalMs: delay } : {}), ...(jitter !== undefined ? { jitterMs: jitter } : {}) } })); + setPacingModelId(""); setPacingModelRpm(""); setPacingModelDelay(""); setPacingModelJitter(""); }; return ( @@ -330,6 +355,38 @@ export default function ProviderSettings({ {t("pws.providerId")} + {item.name === "google-antigravity" && ( +
+ {t("pws.antigravityTlsTitle")} + + + + {t("pws.antigravityTlsStatus", { status: item.tlsProfileStatus === "active" ? t("pws.antigravityTlsActive") : item.tlsProfileStatus === "fallback" ? t("pws.antigravityTlsFallback") : t("pws.antigravityTlsDisabled") })} + +
+ )}