Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs-site/src/content/docs/ja/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ interface ProviderAdapter {
プロバイダーが明示的に alias を設定しない限り、`xhigh` と `max` は異なるラベルのまま保ちます。`provider.noReasoningModels` に含まれる id には値を **一切送りません**。
- `delta.content`(テキスト)、`delta.reasoning_content`(thinking)、`delta.tool_calls[]` を
ストリーミングし、`usage` を収集します。
- ClinePass は、ライブ検証済みのゲートウェイ形式 `reasoning: { enabled: true, effort: "low" }`
- ClinePass は、ライブ検証済みのゲートウェイ形式 `reasoning: { enabled: true, effort }`
(reasoning を無効にする場合は `{ enabled: false }`)を使用します。公開 API ドキュメントには
現在このリクエスト形式が明記されていません。アダプターは他の effort リクエストを検証済みの
`low` に調整し、`delta.reasoning_content` または `delta.reasoning` を reasoning delta として扱い、
`stream_options.include_usage` でストリーム usage を要求し、非ストリームのレスポンス envelope からも usage を読み取ります。
現在このリクエスト形式が明記されていません。アダプターは要求された `low`、`medium`、`high`、
`xhigh`、`max` tier をそのまま保持し、`delta.reasoning_content` または `delta.reasoning` を
reasoning delta として扱い、`stream_options.include_usage` でストリーム usage を要求し、非ストリームのレスポンス envelope からも usage を読み取ります。

## `openai-responses`

Expand Down
8 changes: 4 additions & 4 deletions docs-site/src/content/docs/ko/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ interface ProviderAdapter {
유지합니다. `provider.noReasoningModels`에 든 id에는 값을 **아예 보내지 않습니다**.
- `delta.content`(텍스트), `delta.reasoning_content`(thinking), `delta.tool_calls[]`를
스트리밍하고 `usage`를 수집합니다.
- ClinePass는 라이브로 검증된 게이트웨이 형식 `reasoning: { enabled: true, effort: "low" }`을
사용하며, reasoning을 끌 때는 `{ enabled: false }`를 사용합니다. 공개 API 문서에는 현재 이 요청
형식이 명시되어 있지 않습니다. 어댑터는 다른 effort 요청을 검증된 `low`로 조정하고,
`delta.reasoning_content` 또는 `delta.reasoning`을 reasoning delta로 처리하며,
- ClinePass는 라이브로 검증된 게이트웨이 형식 `reasoning: { enabled: true, effort }`을 사용하며,
reasoning을 끌 때는 `{ enabled: false }`를 사용합니다. 공개 API 문서에는 현재 이 요청 형식이
명시되어 있지 않습니다. 어댑터는 요청한 `low`, `medium`, `high`, `xhigh`, `max` 단계를 그대로
유지하고, `delta.reasoning_content` 또는 `delta.reasoning`을 reasoning delta로 처리하며,
`stream_options.include_usage`로 스트림 usage를 요청하고 비스트림 응답 envelope에서도 usage를 읽습니다.

## `openai-responses`
Expand Down
10 changes: 5 additions & 5 deletions docs-site/src/content/docs/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ provider — xAI, Kimi, DeepSeek, GLM, Groq, OpenRouter, Ollama (local & cloud),
adapter **omits it entirely** for ids in `provider.noReasoningModels`.
- Streams `delta.content` (text), `delta.reasoning_content` (thinking), and `delta.tool_calls[]`;
collects `usage`.
- ClinePass uses the live-verified gateway format `reasoning: { enabled: true, effort: "low" }`
(or `{ enabled: false }` when reasoning is disabled); its public API docs do not currently specify
this request shape. The adapter clamps other effort requests to the verified `low` tier, accepts
reasoning deltas from either `delta.reasoning_content` or `delta.reasoning`, requests streamed
usage with `stream_options.include_usage`, and reads usage from non-stream response envelopes.
- ClinePass uses the live-verified gateway format `reasoning: { enabled: true, effort }` (or
`{ enabled: false }` when reasoning is disabled); its public API docs do not currently specify
this request shape. The adapter preserves requested `low`, `medium`, `high`, `xhigh`, and `max`
tiers, accepts reasoning deltas from either `delta.reasoning_content` or `delta.reasoning`, requests
streamed usage with `stream_options.include_usage`, and reads usage from non-stream response envelopes.

## `openai-responses`

Expand Down
10 changes: 5 additions & 5 deletions docs-site/src/content/docs/ru/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ interface ProviderAdapter {
- Стримит `delta.content` (текст), `delta.reasoning_content` (thinking) и `delta.tool_calls[]`;
собирает `usage`.
- ClinePass использует проверенный на живом API формат шлюза
`reasoning: { enabled: true, effort: "low" }` (или `{ enabled: false }`, когда reasoning отключён);
в публичной документации API этот формат запроса пока не указан. Адаптер прижимает другие
уровни effort к проверенному `low`, принимает reasoning delta из `delta.reasoning_content` или
`delta.reasoning`, запрашивает usage потока через `stream_options.include_usage` и читает usage
из envelope нестримингового ответа.
`reasoning: { enabled: true, effort }` (или `{ enabled: false }`, когда reasoning отключён);
в публичной документации API этот формат запроса пока не указан. Адаптер сохраняет запрошенный
уровень `low`, `medium`, `high`, `xhigh` или `max`, принимает reasoning delta из
`delta.reasoning_content` или `delta.reasoning`, запрашивает usage потока через
`stream_options.include_usage` и читает usage из envelope нестримингового ответа.

## `openai-responses`

Expand Down
9 changes: 4 additions & 5 deletions docs-site/src/content/docs/zh-cn/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ interface ProviderAdapter {
省略**该参数。
- 流式输出 `delta.content`(文本)、`delta.reasoning_content`(thinking)和
`delta.tool_calls[]`,并收集 `usage`。
- ClinePass 使用经实时验证的网关格式 `reasoning: { enabled: true, effort: "low" }`;关闭
reasoning 时使用 `{ enabled: false }`。其公开 API 文档目前没有说明这一请求格式。adapter 会把
其他 effort 请求限制到已验证的 `low`,把 `delta.reasoning_content` 或 `delta.reasoning`
作为 reasoning delta,通过 `stream_options.include_usage` 请求流式 usage,并从非流式响应
envelope 中读取 usage。
- ClinePass 使用经实时验证的网关格式 `reasoning: { enabled: true, effort }`;关闭 reasoning 时使用
`{ enabled: false }`。其公开 API 文档目前没有说明这一请求格式。adapter 会保留请求的 `low`、
`medium`、`high`、`xhigh` 或 `max` 档位,把 `delta.reasoning_content` 或 `delta.reasoning`
作为 reasoning delta,通过 `stream_options.include_usage` 请求流式 usage,并从非流式响应 envelope 中读取 usage。

## `openai-responses`

Expand Down
6 changes: 4 additions & 2 deletions docs-site/src/content/docs/zh-tw/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,10 @@ preset。儀表板的 **Add provider** picker 會開啟 key provider 的 dashboa
與 [Chat Completions endpoint](https://docs.cline.bot/api/chat-completions),由 Cline Bot Inc. 依
[Cline terms](https://cline.bot/tos) 提供。像 `cline-pass/cline-pass/kimi-k3` 這類 routed id 是刻意設計:
第一段選擇 opencodex provider,後面的 `cline-pass/kimi-k3` 才是送往上游的完整 model slug。ClinePass
quota 由帳號共用,包含 rolling 5-hour、weekly 與 monthly limit。opencodex 目前只宣告 live-verified
`low` reasoning tier;更高 requested tier 會 clamp 到 `low`,直到 gateway 發布或驗證更廣的 ladder。
quota 由帳號共用,包含 rolling 5-hour、weekly 與 monthly limit。2026-08-13 的 live probe 已確認所有
靜態 ClinePass model 在 gateway input 都接受 `low`、`medium`、`high`、`xhigh` 與 `max`。符合 registry
transport 的 canonical ClinePass 設定會保留 requested tier;同名 custom provider 則保留明確設定的
reasoning configuration。backend-specific normalization 由 ClinePass 負責。

**Cline** 使用相同 API key 與 endpoint,但採 pay-as-you-go 用量計費,可使用 100+ 模型,包括
OpenRouter 風格 id,例如 `anthropic/claude-sonnet-4-6`。Cline 的 promotional free model 只提供給 Cline
Expand Down
12 changes: 11 additions & 1 deletion src/providers/derive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,14 @@ function enrichReasoningSummariesByDestination(prov: OcxProviderConfig): void {
applyReasoningSummaryDefaults(prov, destination?.modelSupportsReasoningSummaries);
}

/** Repair the exact low-only ClinePass ladder generated by older key-login presets. */
export function hasLegacyClinePassReasoningEfforts(name: string, prov: OcxProviderConfig): boolean {
return name === "cline-pass"
&& prov.reasoningWireFormat === "gateway-object"
&& prov.reasoningEfforts?.length === 1
&& prov.reasoningEfforts[0] === "low";
}

export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig): void {
const entry = PROVIDER_REGISTRY.find(row => row.id === name);
if (!entry || !providerMatchesRegistryTransportWithStaticGuards(name, prov)) {
Expand Down Expand Up @@ -426,7 +434,9 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
if (seed.modelInputModalities) prov.modelInputModalities = fillRecordOfArrays(seed.modelInputModalities, prov.modelInputModalities);
if (prov.defaultMaxOutputTokens === undefined && seed.defaultMaxOutputTokens !== undefined) prov.defaultMaxOutputTokens = seed.defaultMaxOutputTokens;
if (!prov.modelMaxOutputTokens && seed.modelMaxOutputTokens) prov.modelMaxOutputTokens = { ...seed.modelMaxOutputTokens };
if (!prov.reasoningEfforts && seed.reasoningEfforts) prov.reasoningEfforts = [...seed.reasoningEfforts];
if ((!prov.reasoningEfforts || hasLegacyClinePassReasoningEfforts(name, prov)) && seed.reasoningEfforts) {
prov.reasoningEfforts = [...seed.reasoningEfforts];
}
if (!prov.modelReasoningEfforts && seed.modelReasoningEfforts) prov.modelReasoningEfforts = cloneRecordOfArrays(seed.modelReasoningEfforts);
if (!prov.modelDefaultReasoningEfforts && seed.modelDefaultReasoningEfforts) prov.modelDefaultReasoningEfforts = { ...seed.modelDefaultReasoningEfforts };
if (!prov.reasoningEffortMap && seed.reasoningEffortMap) prov.reasoningEffortMap = { ...seed.reasoningEffortMap };
Expand Down
12 changes: 2 additions & 10 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { hasOwnProvider, resolveEnvValue } from "./config";
import { assertProviderDestinationAllowed } from "./lib/destination-policy";
import { redactSecretString, redactUrlForLog } from "./lib/redact";
import { PROVIDER_REGISTRY, providerCodexAccountMode } from "./providers/registry";
import { applyDirectReasoningEffortContracts } from "./providers/derive";
import { applyDirectReasoningEffortContracts, hasLegacyClinePassReasoningEfforts } from "./providers/derive";
import {
providerMatchesRegistryTransportWithStaticGuards,
providerSupportsLiveModelDiscovery,
Expand Down Expand Up @@ -286,14 +286,6 @@ export function routedProviderConfig(providerName: string, provider: OcxProvider
const modelReasoningEffortMap = mergeNestedRecord(registryEntry.modelReasoningEffortMap, provider.modelReasoningEffortMap);
const modelReasoningEfforts = mergeStringArrayRecord(registryEntry.modelReasoningEfforts, provider.modelReasoningEfforts);
const modelDefaultReasoningEfforts = mergeRecordFill(registryEntry.modelDefaultReasoningEfforts, provider.modelDefaultReasoningEfforts);
// Key-login used to persist this exact low-only ClinePass capability seed. Once the gateway's
// wider input ladder was live-verified, leaving that generated row untouched would keep old
// installs clamped forever. This branch is reached only after canonical transport matching, so
// same-named custom destinations and every other explicit ladder still retain user precedence.
const repairLegacyClinePassReasoningEfforts = providerName === "cline-pass"
&& provider.reasoningWireFormat === "gateway-object"
&& provider.reasoningEfforts?.length === 1
&& provider.reasoningEfforts[0] === "low";
const modelContextWindows = providerName === OPENAI_API_PROVIDER_ID
? mergePositiveNumberCaps(registryEntry.modelContextWindows, provider.modelContextWindows)
: mergeRecordFill(registryEntry.modelContextWindows, provider.modelContextWindows);
Expand Down Expand Up @@ -374,7 +366,7 @@ export function routedProviderConfig(providerName: string, provider: OcxProvider
...(provider.project === undefined && registryEntry.project !== undefined ? { project: registryEntry.project } : {}),
...(provider.location === undefined && registryEntry.location !== undefined ? { location: registryEntry.location } : {}),
...(provider.contextWindow === undefined && registryEntry.contextWindow !== undefined ? { contextWindow: registryEntry.contextWindow } : {}),
...((provider.reasoningEfforts === undefined || repairLegacyClinePassReasoningEfforts)
...((provider.reasoningEfforts === undefined || hasLegacyClinePassReasoningEfforts(providerName, provider))
&& registryEntry.reasoningEfforts !== undefined
? { reasoningEfforts: [...registryEntry.reasoningEfforts] }
: {}),
Expand Down
15 changes: 14 additions & 1 deletion tests/cline-pass-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test";
import { createOpenAIChatAdapter } from "../src/adapters/openai-chat";
import { createTranslatorBudget } from "../src/lib/translator-budget";
import { KEY_LOGIN_PROVIDERS } from "../src/oauth/key-providers";
import { providerConfigSeed } from "../src/providers/derive";
import { enrichProviderFromRegistry, providerConfigSeed } from "../src/providers/derive";
import { PROVIDER_REGISTRY } from "../src/providers/registry";
import { routeModel } from "../src/router";
import type { OcxConfig, OcxParsedRequest } from "../src/types";
Expand Down Expand Up @@ -88,6 +88,19 @@ describe("ClinePass provider", () => {
expect(seed).not.toHaveProperty("preserveCustomDestination");
});

test("catalog enrichment repairs the low-only ladder persisted by older ClinePass presets", () => {
const stale = providerConfigSeed(registryEntry());
stale.reasoningEfforts = ["low"];

enrichProviderFromRegistry("cline-pass", stale);

expect(stale.reasoningEfforts).toEqual(["low", "medium", "high", "xhigh", "max"]);

const custom = { ...stale, baseUrl: "https://custom.example/v1", reasoningEfforts: ["low"] };
enrichProviderFromRegistry("cline-pass", custom);
expect(custom.reasoningEfforts).toEqual(["low"]);
});

test("routing keeps the full upstream model slug and emits the Cline gateway reasoning object", () => {
const config: OcxConfig = {
port: 10100,
Expand Down
Loading