From 53702cacd6a0187017f8a27f500f459d763fcd56 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 13:43:55 +0900 Subject: [PATCH 1/5] fix(combos): normalize target reasoning without losing unknown ladders Carry #4319 with native raw-ladder distinction and regression coverage. Co-authored-by: Keito Itagaki <171206780+ke-1t@users.noreply.github.com> --- docs-site/src/content/docs/guides/combos.md | 11 ++- src/adapters/openai-chat.ts | 4 + src/combos/request.ts | 20 ++++- src/server/responses/core.ts | 1 + src/types/config.ts | 5 +- structure/catalog.md | 5 ++ structure/config.md | 2 +- structure/data-planes/inbound-compat.md | 3 + structure/providers/chat-compat.md | 4 +- structure/runtime.md | 2 + structure/transports/inventory.md | 2 +- structure/transports/responses.md | 9 +++ .../openai/openai-chat-hardening.test.ts | 39 +++++++++ tests/codex-integration/combos.test.ts | 79 ++++++++++++++++++- .../server/server-combo-failover-e2e.test.ts | 54 +++++++++++++ 15 files changed, 226 insertions(+), 14 deletions(-) diff --git a/docs-site/src/content/docs/guides/combos.md b/docs-site/src/content/docs/guides/combos.md index ef5ccde16c..c497ec918f 100644 --- a/docs-site/src/content/docs/guides/combos.md +++ b/docs-site/src/content/docs/guides/combos.md @@ -297,9 +297,12 @@ as wildcards in both modes. } ``` -The default is `"strict"`, which keeps the original behavior. This setting changes published -catalog metadata only — it does not change target order, failover policy, or which effort a given -target receives at dispatch. In the dashboard it is the **Adaptive reasoning ladder** switch in a +The default is `"strict"`, which keeps the original picker behavior. This setting does not change +target order or failover policy. At dispatch, an explicitly empty target ladder has its unsupported +effort/thinking controls removed in either mode while preserving supported non-effort reasoning fields +such as `reasoning.summary`; `"adaptive"` applies the same normalization to an unknown target +capability, while known non-empty targets keep their existing per-target effort resolution. +In the dashboard it is the **Adaptive reasoning ladder** switch in a combo's Capabilities section. ## Image / multimodal capability @@ -414,7 +417,7 @@ Combos are stored in the top-level `combos` object, keyed by combo id: | `cooldownMs` | No | unset → upstream fallback (5 s for request-rate 429 codes `1302`/`1305`, otherwise 60 s) | Integer from 1 to 600000. When set, applies as the per-target cooldown whenever no usable upstream `Retry-After` or Codex reset signal exists, including request-rate 429s; when unset, uses the upstream fallback. | | `waitForCooldownMs` | No | `0` | Integer from 0 to 600000. Maximum time to wait for the earliest eligible cooling target before returning `combo_unavailable`; abort cancels the wait. | | `defaultEffort` | No | `null` | `low`, `medium`, `high`, `xhigh`, `max`, or `ultra`; applied only when the caller omits effort and the target advertises support. | -| `reasoningEffortMode` | No | `"strict"` | `"strict"` intersects every known target ladder, so one target advertising no effort control empties the combo's picker. `"adaptive"` excludes those empty ladders from the published intersection. Metadata only; dispatch is unchanged. | +| `reasoningEffortMode` | No | `"strict"` | `"strict"` intersects every known target ladder, so one target advertising no effort control empties the combo's picker. `"adaptive"` excludes those empty ladders from the published intersection. At dispatch, explicit empty or adaptive unknown ladders remove unsupported effort/thinking controls while preserving supported non-effort reasoning fields such as `reasoning.summary`; known non-empty targets keep existing effort resolution. | | `imageInput` | No | `"auto"` | `"auto"` or `"disabled"`. `"auto"` publishes image support only when every target supports images; `"disabled"` forces text-only (drops image from published modalities and rejects image-bearing requests before dispatch). | | `alias` | No | none | Optional trimmed public model id; use the alias rules above. An empty value is stored as no alias. | | `nativeAlias` | No | `false` | Explicitly permit a currently supported bare native `alias` to take routing and catalog precedence. Never inferred from the alias. | diff --git a/src/adapters/openai-chat.ts b/src/adapters/openai-chat.ts index e338d845ad..e4136ecb3e 100644 --- a/src/adapters/openai-chat.ts +++ b/src/adapters/openai-chat.ts @@ -130,6 +130,10 @@ export function buildOpenAIChatPassthroughRequest( for (const field of CHAT_PASSTHROUGH_FIELDS) { if (rawBody[field] !== undefined) body[field] = rawBody[field]; } + const rawEfforts = modelRecordValue(provider.modelReasoningEfforts, modelId) ?? provider.reasoningEfforts; + if (modelInList(provider.noReasoningModels, modelId) || rawEfforts?.length === 0) { + delete body.reasoning_effort; + } const openRouterRouting = resolveOpenRouterRouting(provider, modelId); if (openRouterRouting) body.provider = openRouterProviderPayload(openRouterRouting); diff --git a/src/combos/request.ts b/src/combos/request.ts index abafccc525..63c5ba7fca 100644 --- a/src/combos/request.ts +++ b/src/combos/request.ts @@ -1,4 +1,4 @@ -import type { OcxComboDefaultEffort, OcxComboTarget, OcxConfig } from "../types"; +import type { OcxComboDefaultEffort, OcxComboReasoningEffortMode, OcxComboTarget, OcxConfig } from "../types"; import { resolveEffortAtOrBelow } from "../reasoning-effort"; import { resolveComboId } from "./types"; @@ -59,9 +59,14 @@ export function concreteComboRequestBody( target: Pick, defaultEffort: OcxComboDefaultEffort | null, targetReasoningEfforts: readonly string[] | undefined, + reasoningEffortMode: OcxComboReasoningEffortMode = "strict", ): Record { const clone = structuredClone(body) as Record; clone.model = `${target.provider}/${target.model}`; + if (targetReasoningEfforts?.length === 0 + || (reasoningEffortMode === "adaptive" && targetReasoningEfforts === undefined)) { + stripUnsupportedReasoningControls(clone); + } if (!defaultEffort) return clone; const reasoning = clone.reasoning; const needsDefault = reasoning === undefined || ( @@ -104,3 +109,16 @@ export function concreteComboRequestBody( } return clone; } + +function stripUnsupportedReasoningControls(body: Record): void { + const reasoning = body.reasoning; + if (reasoning && typeof reasoning === "object" && !Array.isArray(reasoning)) { + const next = { ...(reasoning as Record) }; + delete next.effort; + if (Object.keys(next).length > 0) body.reasoning = next; + else delete body.reasoning; + } + delete body.reasoning_effort; + delete body.thinking_budget; + delete body.thinking; +} diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index 8b499d5111..08c561d75e 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -2927,6 +2927,7 @@ export async function handleComboResponses( pick.target, comboDefaultEffort(config, comboId), supportedLadderFor({ provider: targetRoute.provider, modelId: targetRoute.modelId }), + combo.reasoningEffortMode, ); const childHeaders = buildComboChildHeaders(req.headers); const childRequest = new Request(req.url, { diff --git a/src/types/config.ts b/src/types/config.ts index 52c4ed8b0e..a8d925188c 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -967,8 +967,9 @@ export type OcxComboDefaultEffort = "low" | "medium" | "high" | "xhigh" | "max" * advertises no effort control (`reasoningEfforts: []`) empties the combo's picker. * `adaptive` excludes those empty ladders from the published intersection, keeping the * control usable for a mixed-capability group. Unknown (`undefined`) ladders stay - * wildcards in both modes. Dispatch is unchanged: each concrete target still resolves - * its own effort at request time. + * wildcards in both modes. An explicit empty ladder removes unsupported effort controls + * in either mode; adaptive dispatch also removes them before sending to an unknown target, + * while each known target still resolves its own effort. */ export type OcxComboReasoningEffortMode = "strict" | "adaptive"; diff --git a/structure/catalog.md b/structure/catalog.md index eda6f26e80..b9b5d35eb3 100644 --- a/structure/catalog.md +++ b/structure/catalog.md @@ -240,6 +240,11 @@ wire-clamps ultra/max to each model's real top rung (e.g. gpt-5.5 ultra → xhig (`src/server/effort-policy.ts`): they lower or preserve the requested effort rather than rejecting the request, and they never raise it. +Combo dispatch reads the final target ladder through the same `supportedLadderFor` authority. An +explicit empty ladder means that target receives no effort control; an unknown ladder receives no +parent effort controls only when the combo opts into `reasoningEffortMode: "adaptive"`. Known +non-empty ladders continue through the existing per-target resolution. + The `ocx effort` CLI accepts only the same canonical cap ladder before live probing or persistence. Its status output preserves unsupported legacy cap values and reports that those fields are ignored; the read does not normalize or migrate them, and an ignored subagent field does not disable a valid diff --git a/structure/config.md b/structure/config.md index 4f532e789e..4fc23a46c6 100644 --- a/structure/config.md +++ b/structure/config.md @@ -45,7 +45,7 @@ matters for maintainers is which groups exist and who resolves them: | Group | Keys | Resolution rule | | --- | --- | --- | | Listener | `port`, `hostname` | The listener owns the port; `runtime-port.json` reports where it actually landed. | -| Routing | `defaultProvider`, `providers`, per-provider `selectedModels` | Explicit `provider/model` wins over `defaultProvider`. | +| Routing | `defaultProvider`, `providers`, per-provider `selectedModels`, `combos` | Explicit `provider/model` wins over `defaultProvider`; combo dispatch uses the selected target's existing capability ladder and does not create a second catalog authority. | | Catalog | `disabledModels`, `customModels`, `modelCacheTtlMs`, `providerContextCaps`, `contextCapValue`, per-provider `modelDisplayNames`, `codexAccountNamespaces`, `codexAccountPickerEnabled` | Catalog state is derived; config only records intent. Exact provider model display names are durable display only overlays. The picker flag is an explicit visibility override, while selector mappings remain the durable exact-routing contract. | | Retained state | `appOwnedMemoryBudgetMb` | Process-wide eviction target for app-owned logs, caches, blobs, and continuation payloads. Default 256 MiB, valid 64..4096; pinned state may temporarily exceed the target, but every pin-capable store has a finite local cap and their documented aggregate stays below `APP_OWNED_WORST_CASE_PINNED_BYTES` (512 MiB). Neither value caps RSS or native runtime memory. | | Transport | stream mode, timeouts, proxy settings, `websockets`, `emptyCompletionRetry` | `streamMode` persists in config.json; Windows services need a persisted input, and macOS uses it for explicit eager-relay opt-in. Empty-completion replay is an explicit top-level opt-in because its second upstream request may be billable. | diff --git a/structure/data-planes/inbound-compat.md b/structure/data-planes/inbound-compat.md index ee84baebee..88a5510042 100644 --- a/structure/data-planes/inbound-compat.md +++ b/structure/data-planes/inbound-compat.md @@ -19,6 +19,9 @@ the native passthrough there is no canonical Fast injection and no wire mapping: and `fastMode` injects nothing here. Resolved-Fast-policy injection applies only to routes that take the Chat -> Responses -> Chat bridge below. `parallel_tool_calls` is emitted only for providers opted into parallel tools (or pinned false by the existing provider opt-out contract). +The native passthrough still applies the existing model capability authority to reasoning: an +explicit empty ladder removes caller `reasoning_effort`, while an unknown ladder remains +unclassified. This guard does not alter the separate raw service-tier contract. Combo/policy routes and requests that need Responses-only hosted tools, continuation, background, or storage semantics retain the existing Chat -> Responses -> Chat bridge. diff --git a/structure/providers/chat-compat.md b/structure/providers/chat-compat.md index e0b87add2d..efaac6c8e0 100644 --- a/structure/providers/chat-compat.md +++ b/structure/providers/chat-compat.md @@ -22,7 +22,9 @@ content converter after validation and imports no optional subsystem. Stateful developer-guidance injection reuses that validator for its raw insertion boundary, so parsed messages and stored raw history retain the same task/guidance order. -Native OpenAI passthrough sanitizes routed reasoning history so `reasoning` input items do not send +Native OpenAI passthrough consults the existing configured capability ladder before forwarding +`reasoning_effort`; an explicitly empty ladder removes that unsupported control while an unknown +ladder remains unclassified. It also sanitizes routed reasoning history so `reasoning` input items do not send non-empty `content` arrays to upstream models that reject them. Chat Completions bridging repairs orphan `toolResult` messages by inserting a synthetic assistant `tool_call` before tool messages. It also repairs the opposite direction (260718): an assistant `tool_calls` round left dangling — diff --git a/structure/runtime.md b/structure/runtime.md index 89276a1839..669016a8a7 100644 --- a/structure/runtime.md +++ b/structure/runtime.md @@ -146,6 +146,8 @@ The server exposes `POST /api/stop` which restores native Codex config, stops an | `src/providers/registry.ts` | Canonical provider presets for CLI, dashboard, OAuth, key providers, and metadata. | | `src/providers/derive.ts` | Enrichment from provider presets into user config. | | `src/oauth/` | OAuth providers, token storage, refresh, and auth-token resolution. The login callback listener binds a per-provider FIXED loopback port, so consecutive logins reuse the same number; every response it sends ends its connection (`Connection: close`, including non-callback paths such as a stray `/favicon.ico` 404). Stopping the listener does not close an established socket, so without that a pooled client would deliver the next login's callback to the retired flow, which rejects the unknown state as a CSRF mismatch while the live flow waits. | +| `src/oauth/` | OAuth providers, token storage, refresh, and auth-token resolution. | +| `src/combos/request.ts` | Clones each selected combo target request and applies the existing target capability ladder: adaptive unknown targets and explicit empty ladders receive no unsupported reasoning/thinking controls, while known ladders retain per-target resolution. | | `src/adapters/openai-responses.ts` | Native OpenAI/ChatGPT Responses passthrough. | | `src/adapters/openai-chat.ts` | OpenAI-compatible Chat Completions bridge. | | `src/adapters/anthropic.ts` | Anthropic Messages bridge. | diff --git a/structure/transports/inventory.md b/structure/transports/inventory.md index ec75ff03d4..60328ab2cb 100644 --- a/structure/transports/inventory.md +++ b/structure/transports/inventory.md @@ -14,7 +14,7 @@ surface is listed here so a maintainer can find the owner without grepping: | Adapter execution support | `src/adapters/run-turn-queue.ts`, `src/adapters/tool-catalog-nudge.ts`, `src/adapters/identity.ts`, `src/adapters/image.ts`, `src/adapters/upstream-http-error.ts` | Shared machinery: turn ordering, tool-catalog nudging, client fingerprinting, image conversion, upstream error normalization. | | Cursor (beyond the sections above) | `src/adapters/cursor/live-transport.ts`, `src/adapters/cursor/http1-bidi.ts`, `src/adapters/cursor/live-models.ts`, `src/adapters/cursor/transport-retry.ts`, `src/adapters/cursor/mcp-manager.ts`, `src/adapters/cursor/thread-continuity.ts`, `src/adapters/cursor/checkpoint-store.ts` | Thread continuity is the point: a retry must not start a new Cursor thread, and a validated checkpoint must not rebuild the full root history. HTTP/2 remains the default; an explicit `http1.1`/`h1` pin maps the bidi run onto Cursor's `RunSSE` receive stream plus sequenced `BidiAppend` sends, and applies to live discovery too. | | Claude Messages | `src/server/claude-messages.ts` | Routed translation, a native Anthropic passthrough branch, and `count_tokens`. | -| Chat Completions inbound | `src/server/chat-completions.ts`, `src/chat/` | Inbound translation onto the same routing pipeline. The content mapper preserves image URLs and supported detail, including screenshot-bearing tool results; target adapters own image placement on their wire. Image-free tool results stay strings. | +| Chat Completions inbound | `src/server/chat-completions.ts`, `src/chat/`, `src/adapters/openai-chat.ts` | Inbound translation onto the same routing pipeline. The content mapper preserves image URLs and supported detail, including screenshot-bearing tool results; target adapters own image placement on their wire. Image-free tool results stay strings. Native passthrough consults the existing model capability ladder before forwarding `reasoning_effort`. | | Hosted search relay | `src/server/search.ts` | Direct relay; distinct from the web-search sidecar loop below. | | Image/video generation loop | `src/images/loop.ts`, `src/images/plan.ts`, `src/images/fulfill.ts`, `src/images/xai-client.ts`, `src/images/xai-video-client.ts`, `src/images/artifacts.ts` | A provider-returned image URL is downloaded into a local artifact once, then served locally; warnings stay URL-free because provider CDN URLs may embed credentials. | | GitHub Copilot | `src/providers/xai-transport.ts` (`resolveProviderTransport`), `src/providers/github-copilot-transport.ts` | `resolveProviderTransport` selects the Copilot transport when the routed provider name is `github-copilot`; the Copilot module then resolves its headers and base URL, and the registry seeds the provider row and model fallback. | diff --git a/structure/transports/responses.md b/structure/transports/responses.md index 6e975af2ac..62eb0eff07 100644 --- a/structure/transports/responses.md +++ b/structure/transports/responses.md @@ -489,6 +489,15 @@ combo whose remaining eligible targets use other providers. > Decision record: [ADR-0070](../decisions/ADR-0070-same-provider-combo-quota-fallback.md) +## Combo per-target reasoning controls + +`src/server/responses/core.ts` passes the combo's `reasoningEffortMode` and the final target's +`supportedLadderFor` result to `src/combos/request.ts` before adapter parsing. Explicit empty +capability ladders remove effort and thinking controls in every combo mode; adaptive mode also +removes those controls for unknown ladders and preserves `reasoning.summary`. Known non-empty +ladders retain the existing per-target effort resolution. This request normalization does not +change target order, attempt accounting, or the existing provider-400 failover classification. + ## Combo streaming commit boundary An HTTP 200 does not by itself commit a streaming combo child. The combo parent runs the child's diff --git a/tests/adapters/openai/openai-chat-hardening.test.ts b/tests/adapters/openai/openai-chat-hardening.test.ts index b051817469..3a8fe98f03 100644 --- a/tests/adapters/openai/openai-chat-hardening.test.ts +++ b/tests/adapters/openai/openai-chat-hardening.test.ts @@ -140,6 +140,23 @@ describe("AgentRouter openai-chat compatibility", () => { expect(body.messages[0]?.content.map(part => part.text)).toEqual([preamble, "responda somente: OK"]); expect(rawBody.messages[0]?.content).toBe("responda somente: OK"); }); + + test("passthrough chat drops reasoning_effort for an explicitly empty capability ladder", () => { + const rawBody = { + messages: [{ role: "user", content: "hi" }], + reasoning_effort: "xhigh", + }; + const request = buildOpenAIChatPassthroughRequest( + provider({ reasoningEfforts: [] }), + rawBody, + "test-model", + false, + ); + const body = JSON.parse(request.body as string) as Record; + + expect(body).not.toHaveProperty("reasoning_effort"); + expect(rawBody.reasoning_effort).toBe("xhigh"); + }); }); function parsed(): OcxParsedRequest { @@ -1455,3 +1472,25 @@ test("tool-call deltas emit heartbeats so a long buffering phase is not read as expect(visible.at(-1)).toMatchObject({ type: "done" }); }); }); + + +describe("native Chat raw reasoning declarations", () => { + const raw = { messages: [{ role: "user", content: "hello" }], reasoning_effort: "enabled" }; + function wire(overrides: Partial) { + const request = buildOpenAIChatPassthroughRequest({ + adapter: "openai-chat", baseUrl: "https://example.test/v1", ...overrides, + }, raw, "target", false); + return JSON.parse(request.body as string) as Record; + } + test("preserves a nonempty wire-only ladder as unknown", () => { + expect(wire({ reasoningEfforts: ["enabled"] }).reasoning_effort).toBe("enabled"); + expect(wire({ reasoningEfforts: [], modelReasoningEfforts: { target: ["enabled"] } }).reasoning_effort).toBe("enabled"); + }); + test("honors explicit empty model overrides over a provider ladder", () => { + expect(wire({ reasoningEfforts: ["high"], modelReasoningEfforts: { target: [] } }).reasoning_effort).toBeUndefined(); + }); + test("honors noReasoningModels over a nonempty model ladder without mutating input", () => { + expect(wire({ noReasoningModels: ["target"], modelReasoningEfforts: { target: ["high"] } }).reasoning_effort).toBeUndefined(); + expect(raw.reasoning_effort).toBe("enabled"); + }); +}); diff --git a/tests/codex-integration/combos.test.ts b/tests/codex-integration/combos.test.ts index 76e4f26ca9..0bc1230e0f 100644 --- a/tests/codex-integration/combos.test.ts +++ b/tests/codex-integration/combos.test.ts @@ -297,22 +297,93 @@ describe("combo request cloning", () => { expect(concrete.input).not.toBe(raw.input); }); - test("combo default respects client-owned ignored reasoning values", () => { + test("combo target capability strips unsupported client reasoning controls", () => { expect(concreteComboRequestBody({ model: "combo/x", reasoning: null }, target, "high", []).reasoning).toBeNull(); expect(concreteComboRequestBody( { model: "combo/x", reasoning: { effort: "" } }, target, "high", [], - ).reasoning).toEqual({ effort: "" }); + ).reasoning).toBeUndefined(); expect(concreteComboRequestBody( { model: "combo/x", reasoning: { effort: "banana" } }, target, "high", [], - ).reasoning).toEqual({ effort: "banana" }); + ).reasoning).toBeUndefined(); expect(concreteComboRequestBody( { model: "combo/x", reasoning: { effort: null } }, target, "high", [], - ).reasoning).toEqual({ effort: null }); + ).reasoning).toBeUndefined(); expect(concreteComboRequestBody( { model: "combo/x", reasoning: { summary: "concise" } }, target, "high", ["high"], ).reasoning).toEqual({ summary: "concise", effort: "high" }); }); + test("adaptive normalization strips unsupported controls for an unknown target while preserving summary", () => { + const raw = { + model: "combo/x", + input: "hi", + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", + thinking_budget: 8192, + thinking: { type: "enabled" }, + }; + const concrete = concreteComboRequestBody(raw, target, null, undefined, "adaptive"); + + expect(concrete).toEqual({ + model: "a/m1", + input: "hi", + reasoning: { summary: "concise" }, + }); + expect(raw).toEqual({ + model: "combo/x", + input: "hi", + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", + thinking_budget: 8192, + thinking: { type: "enabled" }, + }); + }); + + test("strict normalization preserves reasoning controls for an unknown target", () => { + const raw = { + model: "combo/x", + input: "hi", + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", + thinking_budget: 8192, + thinking: { type: "enabled" }, + }; + const concrete = concreteComboRequestBody(raw, target, null, undefined, "strict"); + + expect(concrete).toEqual({ + model: "a/m1", + input: "hi", + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", + thinking_budget: 8192, + thinking: { type: "enabled" }, + }); + }); + + test("explicit empty ladder strips unsupported controls while preserving reasoning summary", () => { + const concrete = concreteComboRequestBody({ + model: "combo/x", + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", + thinking_budget: 8192, + thinking: { type: "enabled" }, + }, target, "high", []); + + expect(concrete).toEqual({ + model: "a/m1", + reasoning: { summary: "concise" }, + }); + }); + + test("adaptive normalization preserves xhigh for a known reasoning ladder", () => { + const concrete = concreteComboRequestBody({ + model: "combo/x", + reasoning: { effort: "xhigh", summary: "concise" }, + }, target, null, ["low", "medium", "high", "xhigh"], "adaptive"); + + expect(concrete.reasoning).toEqual({ effort: "xhigh", summary: "concise" }); + }); + test("omits combo defaults for unset, no-reasoning, and unknown target capabilities", () => { expect(concreteComboRequestBody({ model: "combo/x" }, target, null, ["high"]).reasoning).toBeUndefined(); // An explicitly empty ladder is how a no-reasoning model is expressed. diff --git a/tests/server/server-combo-failover-e2e.test.ts b/tests/server/server-combo-failover-e2e.test.ts index 6f649c52d3..76b90720ba 100644 --- a/tests/server/server-combo-failover-e2e.test.ts +++ b/tests/server/server-combo-failover-e2e.test.ts @@ -1182,6 +1182,60 @@ describe("server combo failover 030 activation matrix", () => { expectMappedReceipt(hydrated[0]!); }); + test("adaptive combo normalizes unknown and empty target capability before the upstream wire", async () => { + const bodies: Array> = []; + const upstream = serve(async request => { + bodies.push(await request.json() as Record); + return chatSuccess("normalized", "m1"); + }); + const request = { + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", + thinking_budget: 8192, + thinking: { type: "enabled" }, + }; + + const unknownResponse = await post( + comboConfig( + { a: provider("openai-chat", baseUrl(upstream), "key-a") }, + undefined, + { reasoningEffortMode: "adaptive" }, + ), + request, + ); + expect(unknownResponse.status).toBe(200); + + const emptyResponse = await post( + comboConfig( + { a: provider("openai-chat", baseUrl(upstream), "key-a", { reasoningEfforts: [] }) }, + undefined, + { reasoningEffortMode: "adaptive" }, + ), + request, + ); + expect(emptyResponse.status).toBe(200); + + const knownResponse = await post( + comboConfig( + { a: provider("openai-chat", baseUrl(upstream), "key-a", { + reasoningEfforts: ["low", "medium", "high", "xhigh"], + }) }, + undefined, + { reasoningEffortMode: "adaptive" }, + ), + request, + ); + expect(knownResponse.status).toBe(200); + + expect(bodies).toHaveLength(3); + for (const body of bodies.slice(0, 2)) { + expect(body).not.toHaveProperty("reasoning_effort"); + expect(body).not.toHaveProperty("thinking_budget"); + expect(body).not.toHaveProperty("thinking"); + } + expect(bodies[2]!.reasoning_effort).toBe("xhigh"); + }); + test("all-target exhaustion promotes the final attempt reasoning wire to the logical row", async () => { const a = serve(() => Response.json({ error: { message: "first overloaded" } }, { status: 503 })); const b = serve(() => Response.json({ error: { message: "last overloaded" } }, { status: 503 })); From 07b47dd2c92c93b81c1359fda33d97fb083ff6b4 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 14:02:59 +0900 Subject: [PATCH 2/5] docs: synchronize all combo reasoning source owners --- structure/adapters/registry.md | 2 ++ structure/clients/claude-desktop.md | 2 ++ structure/data-planes/images.md | 2 ++ structure/gui-and-management-api.md | 2 ++ structure/ops/docs-and-release.md | 2 ++ structure/ops/service-and-sidecars.md | 2 ++ structure/providers/cursor.md | 2 ++ structure/providers/xai-grok.md | 2 ++ structure/runtime.md | 1 - structure/subagents.md | 2 ++ structure/transports/streaming-health.md | 2 ++ 11 files changed, 20 insertions(+), 1 deletion(-) diff --git a/structure/adapters/registry.md b/structure/adapters/registry.md index db26a7cb4d..735514ccc5 100644 --- a/structure/adapters/registry.md +++ b/structure/adapters/registry.md @@ -71,3 +71,5 @@ see [Combo editor routing quota](../gui-and-management-api.md#combo-editor-routi Claude replay carries [Go conversation affinity](../data-planes/inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/clients/claude-desktop.md b/structure/clients/claude-desktop.md index 54689b36a1..cfa1bba58f 100644 --- a/structure/clients/claude-desktop.md +++ b/structure/clients/claude-desktop.md @@ -95,3 +95,5 @@ Config JSON preserves the boolean; only literal true activates the role-changing The lightweight top-level CLI help counts Cline CLI among the fifteen registered export clients; registry parity remains covered by the client help and integration tests. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/data-planes/images.md b/structure/data-planes/images.md index d1d0193048..1d74b0a354 100644 --- a/structure/data-planes/images.md +++ b/structure/data-planes/images.md @@ -80,3 +80,5 @@ see [Combo editor routing quota](../gui-and-management-api.md#combo-editor-routi Claude replay carries [Go conversation affinity](inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md index 82d0ef5d20..af16f1710b 100644 --- a/structure/gui-and-management-api.md +++ b/structure/gui-and-management-api.md @@ -544,3 +544,5 @@ integration IO adapter. Its snapshot fingerprint cannot be checked against provi The existing dashboard file-client maps include Cline CLI and reuse its committed color mark. The export panel labels its download as a settings/catalog bundle; all locales explain that Undo restores both original files. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/ops/docs-and-release.md b/structure/ops/docs-and-release.md index 8e31434039..29f9154fcd 100644 --- a/structure/ops/docs-and-release.md +++ b/structure/ops/docs-and-release.md @@ -318,3 +318,5 @@ The integrations guide documents Cline CLI as a two-file, loopback-only integrat The lightweight top-level CLI help counts Cline CLI among the fifteen registered export clients; registry parity remains covered by the client help and integration tests. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/ops/service-and-sidecars.md b/structure/ops/service-and-sidecars.md index c8ef069110..fd5cfb556b 100644 --- a/structure/ops/service-and-sidecars.md +++ b/structure/ops/service-and-sidecars.md @@ -143,3 +143,5 @@ see [Combo editor routing quota](../gui-and-management-api.md#combo-editor-routi Claude replay carries [Go conversation affinity](../data-planes/inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/providers/cursor.md b/structure/providers/cursor.md index be42793e0b..1231d280c8 100644 --- a/structure/providers/cursor.md +++ b/structure/providers/cursor.md @@ -82,3 +82,5 @@ constraints cannot widen the canonical shape. Bare shell bridge names are reject on the freeform path. Namespaced tools do not acquire bare-shell behavior. Regression coverage lives in `tests/providers/cursor/cursor-tool-definitions.test.ts`. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/providers/xai-grok.md b/structure/providers/xai-grok.md index 5f4217b093..fe7a8b504e 100644 --- a/structure/providers/xai-grok.md +++ b/structure/providers/xai-grok.md @@ -69,3 +69,5 @@ privately to final dispatch; preliminary route selection does not inject Go-only Devin CLI credential path composition in `src/oauth/devin-cli.ts` follows the selected platform: Windows uses Win32 APPDATA paths, other platforms use POSIX XDG-data paths. The explicit absolute override remains verbatim; credential parsing and login behavior are unchanged. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/runtime.md b/structure/runtime.md index 669016a8a7..4450637be3 100644 --- a/structure/runtime.md +++ b/structure/runtime.md @@ -146,7 +146,6 @@ The server exposes `POST /api/stop` which restores native Codex config, stops an | `src/providers/registry.ts` | Canonical provider presets for CLI, dashboard, OAuth, key providers, and metadata. | | `src/providers/derive.ts` | Enrichment from provider presets into user config. | | `src/oauth/` | OAuth providers, token storage, refresh, and auth-token resolution. The login callback listener binds a per-provider FIXED loopback port, so consecutive logins reuse the same number; every response it sends ends its connection (`Connection: close`, including non-callback paths such as a stray `/favicon.ico` 404). Stopping the listener does not close an established socket, so without that a pooled client would deliver the next login's callback to the retired flow, which rejects the unknown state as a CSRF mismatch while the live flow waits. | -| `src/oauth/` | OAuth providers, token storage, refresh, and auth-token resolution. | | `src/combos/request.ts` | Clones each selected combo target request and applies the existing target capability ladder: adaptive unknown targets and explicit empty ladders receive no unsupported reasoning/thinking controls, while known ladders retain per-target resolution. | | `src/adapters/openai-responses.ts` | Native OpenAI/ChatGPT Responses passthrough. | | `src/adapters/openai-chat.ts` | OpenAI-compatible Chat Completions bridge. | diff --git a/structure/subagents.md b/structure/subagents.md index a80af4d990..59ebc68348 100644 --- a/structure/subagents.md +++ b/structure/subagents.md @@ -215,3 +215,5 @@ see [Combo editor routing quota](gui-and-management-api.md#combo-editor-routing- Claude replay carries [Go conversation affinity](data-planes/inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. diff --git a/structure/transports/streaming-health.md b/structure/transports/streaming-health.md index 6a5574c6d2..a7a6ffccae 100644 --- a/structure/transports/streaming-health.md +++ b/structure/transports/streaming-health.md @@ -200,3 +200,5 @@ see [Combo editor routing quota](../gui-and-management-api.md#combo-editor-routi Claude replay carries [Go conversation affinity](../data-planes/inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model. From 926d4123e73af037154ca7df71d335806a9c336e Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 14:28:35 +0900 Subject: [PATCH 3/5] test(combos): verify summary preservation on the Responses wire Document reasoningEffortMode in all seven translated guides. Keep the original Chat-wire regression; Responses summary is not a Chat request field. --- .../src/content/docs/fr/guides/combos.md | 7 +++++ .../src/content/docs/ja/guides/combos.md | 7 +++++ .../src/content/docs/ko/guides/combos.md | 7 +++++ .../src/content/docs/ru/guides/combos.md | 7 +++++ .../src/content/docs/tr/guides/combos.md | 7 +++++ .../src/content/docs/zh-cn/guides/combos.md | 7 +++++ .../src/content/docs/zh-tw/guides/combos.md | 7 +++++ .../server/server-combo-failover-e2e.test.ts | 28 +++++++++++++++++++ 8 files changed, 77 insertions(+) diff --git a/docs-site/src/content/docs/fr/guides/combos.md b/docs-site/src/content/docs/fr/guides/combos.md index 9073372327..5120925d6d 100644 --- a/docs-site/src/content/docs/fr/guides/combos.md +++ b/docs-site/src/content/docs/fr/guides/combos.md @@ -233,6 +233,12 @@ par défaut et laisse le comportement de la cible inchangé. Les valeurs prises `high`, `xhigh`, `max` et `ultra` ; omettez le champ ou réglez-le sur `null` pour laisser l'effort entièrement à l'appelant et la cible. +## Capacités reasoning mixtes + +`reasoningEffortMode` vaut `"strict"` par défaut : le catalogue publie l’intersection des listes effort de toutes les cibles, y compris les listes explicitement vides. `"adaptive"` exclut ces listes vides pour conserver le sélecteur dans un combo mixte. Une liste inconnue ne limite l’intersection dans aucun des deux modes. + +À l’envoi, une liste explicitement vide supprime les paramètres effort et thinking dans les deux modes ; une liste inconnue les supprime uniquement en adaptive. `reasoning.summary` et les autres champs hors effort sont conservés. La résolution des cibles connues non vides reste inchangée. Les cibles inconnues en strict et les déclarations inconnues du native Chat ordinaire conservent les paramètres de l’appelant. L’ajout d’une valeur par défaut ne remplace pas un effort existant, mais cette normalisation peut supprimer les paramètres non pris en charge. + ## Capacité d’entrée d’images / multimodale Par défaut, une combinaison publie l’**intersection** des modalités d’entrée de ses cibles : les images ne @@ -337,6 +343,7 @@ Les combos sont stockés dans l'objet `combos` de niveau supérieur, saisi par l | `strategy` | Non | `"failover"` | Valeurs autorisées : `"failover"`, `"round-robin"`, `"random"`, `"least-used"` et `"reset-window"`. | | `stickyLimit` | Non | `1` | Nombre entier de 1 à 100 requêtes réussies par sélection à tour de rôle. S’applique uniquement à `round-robin`. | | `defaultEffort` | Non | `null` | `low`, `medium`, `high`, `xhigh`, `max` ou `ultra` ; appliqué uniquement lorsque l'appelant omet ses efforts et que la cible annonce son soutien. | +| `reasoningEffortMode` | Non | `"strict"` | `strict` ou `adaptive` ; choisit l’intersection des capacités et la normalisation par cible. | | `imageInput` | Non | `"auto"` | `"auto"` ou `"disabled"`. `"auto"` publie les images uniquement si toutes les cibles les prennent en charge ; `"disabled"` impose le texte seul, retire les images des modalités publiées et rejette les requêtes qui en contiennent avant leur distribution. | | `alias` | Non | aucun | Identifiant de modèle public tronqué facultatif ; utilisez les règles d'alias ci-dessus. Une valeur vide est stockée sans alias. | | `nativeAlias` | Non | `false` | Autoriser explicitement un `alias` natif nu actuellement pris en charge à avoir la priorité sur le routage et le catalogue. Jamais déduit de l'alias. | diff --git a/docs-site/src/content/docs/ja/guides/combos.md b/docs-site/src/content/docs/ja/guides/combos.md index 70c902ec6e..efb2fa6021 100644 --- a/docs-site/src/content/docs/ja/guides/combos.md +++ b/docs-site/src/content/docs/ja/guides/combos.md @@ -149,6 +149,12 @@ ocx combo set balanced \ ターゲットの機能が不明な場合、または設定されたエフォートが含まれていない場合、opencodex はデフォルトを省略し、ターゲット自体の動作を変更しないままにします。サポートされている値は、`low`、`medium`、`high`、`xhigh`、`max`、および `ultra` です。このフィールドを省略するか、`null` に設定して、呼び出し元とターゲットに作業を完全に任せます。 +## 異なる reasoning capability の組み合わせ + +`reasoningEffortMode` の既定値は `"strict"` です。明示的な空リストを含む全対象の effort リストの共通部分を公開します。`"adaptive"` は空リストを共通部分の計算から除外し、混在するコンボでも選択肢を維持します。不明なリストは、どちらのモードでもカタログの共通部分を制限しません。 + +送信時には、明示的な空リストの対象で effort と thinking の制御を両モードとも削除します。不明な対象で削除するのは adaptive のみです。`reasoning.summary` と effort 以外のフィールドは保持し、既知の空でない対象は従来どおり effort を解決します。strict の不明な対象と通常の native Chat の不明な宣言は保持します。既定値の補完は既存の effort を上書きしませんが、この正規化は非対応の制御を削除できます。 + ## 暗号化された v2 サブエージェント タスク Codex v2 サブエージェントには重要な制限が 1 つあります ([第92号](https://github.com/lidge-jun/opencodex/issues/92))。ネイティブの親は、新しく生成されたワーカーのタスクを、ネイティブ ChatGPT バックエンド用に作成された暗号文としてのみ送信できます。外部プロバイダーはそのペイロードを読み取ることができません。 @@ -237,6 +243,7 @@ ocx combo remove --yes | `cooldownMs` |いいえ | 未設定 → アップストリーム フォールバック(リクエストレート 429 コード `1302`/`1305` では 5 秒、それ以外では 60 秒) | 1 ~ 600000 の整数。設定時は、使用可能なアップストリーム `Retry-After` または Codex リセットシグナルがない場合に、リクエストレート 429 を含むターゲットごとのクールダウンとして適用されます。未設定時はアップストリーム フォールバックを使用します。 | | `waitForCooldownMs` |いいえ | `0` | 0 ~ 600000 の整数。最も早く利用可能になる冷却中のターゲットを待ってから `combo_unavailable` を返すまでの最大待機時間。中止すると待機はキャンセルされます。 | | `defaultEffort` |いいえ | `null` | `low`、`medium`、`high`、`xhigh`、`max`、または `ultra`;呼び出し元が努力を省略し、ターゲットがサポートをアドバタイズした場合にのみ適用されます。 | +| `reasoningEffortMode` | いいえ | `"strict"` | `strict` または `adaptive`。混在する capability の共通部分と対象別の制御正規化を選択します。 | | `alias` |いいえ |なし |オプションのトリミングされたパブリック モデル ID。上記のエイリアス ルールを使用します。空の値はエイリアスなしで保存されます。 | | `nativeAlias` |いいえ | `false` | 現在サポートされている bare native alias に routing/catalog の優先権を明示的に与えます。 | | `displayName` |いいえ |なし | catalog 表示専用ラベル。`nativeAlias` が true の場合は必須です。 | diff --git a/docs-site/src/content/docs/ko/guides/combos.md b/docs-site/src/content/docs/ko/guides/combos.md index 80eac32c2d..b7f9165ebb 100644 --- a/docs-site/src/content/docs/ko/guides/combos.md +++ b/docs-site/src/content/docs/ko/guides/combos.md @@ -155,6 +155,12 @@ ocx combo set balanced \ 대상 기능을 알 수 없거나 설정한 effort를 포함하지 않으면 opencodex는 기본값을 생략하고 대상의 동작은 그대로 둡니다. 지원 값은 `low`, `medium`, `high`, `xhigh`, `max`, `ultra`입니다. effort를 호출자와 대상에 완전히 맡기려면 이 필드를 생략하거나 `null`로 설정하십시오. +## 서로 다른 reasoning capability + +`reasoningEffortMode`의 기본값은 `"strict"`입니다. 모든 대상의 effort 목록을 교집합으로 계산하므로 명시적 빈 목록도 반영합니다. `"adaptive"`는 빈 목록을 교집합에서 제외해 혼합 콤보에서도 선택기를 유지합니다. 알 수 없는 목록은 두 모드 모두 카탈로그 교집합을 제한하지 않습니다. + +전송 시 명시적 빈 목록은 두 모드 모두에서 effort·thinking 제어를 제거하고, 알 수 없는 목록은 adaptive에서만 제거합니다. `reasoning.summary`와 다른 비-effort 필드는 보존하며, 알려진 비어 있지 않은 대상은 기존 방식으로 effort를 결정합니다. strict의 unknown 대상과 일반 native Chat의 unknown 선언은 그대로 유지됩니다. 기본값 주입은 기존 effort를 덮어쓰지 않지만, 이 capability 정규화는 지원되지 않는 제어를 제거할 수 있습니다. + ## 암호화된 v2 서브에이전트 작업 Codex v2 서브에이전트에는 중요한 제한이 하나 있습니다([issue #92](https://github.com/lidge-jun/opencodex/issues/92)). 네이티브 부모 프로세스는 새로 생성된 작업자에게 보낼 작업을 네이티브 ChatGPT 백엔드용으로 생성한 암호문으로만 전달할 수 있습니다. 외부 공급자는 그 페이로드를 읽을 수 없습니다. @@ -241,6 +247,7 @@ ocx combo remove --yes | `cooldownMs` | 아니요 | 미설정 → 업스트림 폴백(요청 속도 제한 429 코드 `1302`/`1305`는 5초, 그 외는 60초) | 1에서 600000 사이의 정수입니다. 설정하면 사용 가능한 업스트림 `Retry-After` 또는 Codex 재설정 신호가 없을 때 요청 속도 제한 429를 포함한 대상별 쿨다운으로 적용됩니다. 설정하지 않으면 업스트림 폴백을 사용합니다. | | `waitForCooldownMs` | 아니요 | `0` | 0에서 600000 사이의 정수입니다. `combo_unavailable`을 반환하기 전에 가장 먼저 적합해지는 쿨다운 중인 대상을 기다리는 최대 시간입니다. 중단하면 대기가 취소됩니다. | | `defaultEffort` | 아니요 | `null` | `low`, `medium`, `high`, `xhigh`, `max`, 또는 `ultra`입니다. 호출자가 effort를 생략하고 대상이 지원을 광고할 때만 적용됩니다. | +| `reasoningEffortMode` | 아니요 | `"strict"` | `strict` 또는 `adaptive`; 혼합 capability의 교집합과 대상별 제어 정규화를 선택합니다. | | `alias` | 아니요 | 없음 | 선택적으로 앞뒤 공백을 제거한 공개 모델 ID입니다. 위의 alias 규칙을 따릅니다. 빈 값은 alias 없음으로 저장됩니다. | | `nativeAlias` | 아니요 | `false` | 현재 지원되는 bare native alias가 routing/catalog 우선권을 갖도록 명시적으로 허용합니다. | | `displayName` | 아니요 | 없음 | catalog 표시 전용 label입니다. `nativeAlias`가 true이면 필수입니다. | diff --git a/docs-site/src/content/docs/ru/guides/combos.md b/docs-site/src/content/docs/ru/guides/combos.md index b873f4ed03..d54c03e33e 100644 --- a/docs-site/src/content/docs/ru/guides/combos.md +++ b/docs-site/src/content/docs/ru/guides/combos.md @@ -192,6 +192,12 @@ Failover намеренно ограничен. Он помогает при п `high`, `xhigh`, `max` и `ultra`; опустите поле или задайте `null`, чтобы полностью оставить выбор effort вызывающей стороне и цели. +## Разные возможности reasoning в одном combo + +`reasoningEffortMode` по умолчанию равен `"strict"`: публикуется пересечение списков effort всех целей, включая явно пустые списки. `"adaptive"` исключает пустые списки из пересечения, сохраняя выбор effort для смешанного combo. Неизвестные списки не ограничивают пересечение каталога в обоих режимах. + +При отправке явно пустой список удаляет параметры effort и thinking в обоих режимах; неизвестный список — только в adaptive. `reasoning.summary` и остальные поля, не задающие effort, сохраняются. Для известных непустых списков разрешение effort не меняется. Неизвестные цели в strict и неизвестные объявления обычного native Chat сохраняют параметры вызывающей стороны. Подстановка значения по умолчанию не заменяет существующий effort, но нормализация возможностей может удалить неподдерживаемые параметры. + ## Шифрованные задачи подагентов v2 Есть одно важное ограничение для подагентов Codex v2 @@ -292,6 +298,7 @@ Combo хранятся в объекте верхнего уровня `combos`, | `cooldownMs` | No | не задано → fallback upstream (5 с для rate-limit 429 с кодами `1302`/`1305`, иначе 60 с) | Целое число от 1 до 600000. Если задано, применяется как cooldown каждой цели, когда нет пригодного upstream `Retry-After` или сигнала сброса Codex, включая rate-limit 429; если не задано, используется fallback upstream. | | `waitForCooldownMs` | No | `0` | Целое число от 0 до 600000. Максимальное время ожидания самой ранней подходящей цели в cooldown перед возвратом `combo_unavailable`; отмена запроса отменяет ожидание. | | `defaultEffort` | No | `null` | `low`, `medium`, `high`, `xhigh`, `max` или `ultra`; применяется только когда вызывающая сторона не указала effort, а цель объявляет поддержку. | +| `reasoningEffortMode` | Нет | `"strict"` | `strict` или `adaptive`; задаёт пересечение возможностей и нормализацию параметров конкретной цели. | | `alias` | No | none | Необязательный обрезанный публичный id модели; используйте правила alias выше. Пустое значение хранится как отсутствие alias. | | `nativeAlias` | No | `false` | Явно разрешает поддерживаемому сейчас bare native alias перехватить приоритет routing/catalog только для неквалифицированного id. Bare `gpt-5.6-*` использует учётные данные Codex Pool/Direct; маршруты с квалификатором аккаунта сохраняют свою идентичность, а provider-qualified `openai-apikey/gpt-5.6-*` использует API-ключ и никогда не переходит на native alias. | | `displayName` | No | none | Метка только для отображения в catalog; обязательна при `nativeAlias: true`. | diff --git a/docs-site/src/content/docs/tr/guides/combos.md b/docs-site/src/content/docs/tr/guides/combos.md index b8cd5bad0d..ddb877cbcb 100644 --- a/docs-site/src/content/docs/tr/guides/combos.md +++ b/docs-site/src/content/docs/tr/guides/combos.md @@ -266,6 +266,12 @@ Desteklenen değerler `low`, `medium`, `high`, `xhigh`, `max` ve `ultra`'dır; çabayı tamamen arayana ve hedefe bırakmak için alanı atlayın veya `null` olarak ayarlayın. +## Farklı reasoning yetenekleri + +`reasoningEffortMode` varsayılan olarak `"strict"` kullanır: açıkça boş listeler dahil tüm hedeflerin effort listelerinin kesişimi yayımlanır. `"adaptive"`, karma kombolarda seçiciyi korumak için boş listeleri kesişimden çıkarır. Bilinmeyen listeler her iki modda da katalog kesişimini sınırlamaz. + +Gönderim sırasında açıkça boş liste her iki modda effort ve thinking denetimlerini kaldırır; bilinmeyen liste bunları yalnızca adaptive modunda kaldırır. `reasoning.summary` ve effort dışındaki alanlar korunur. Bilinen, boş olmayan hedeflerin effort çözümü değişmez. strict modundaki bilinmeyen hedefler ve normal native Chat bilinmeyen bildirimleri çağıranın denetimlerini korur. Varsayılan değer ekleme mevcut effort değerini değiştirmez; yetenek normalizasyonu desteklenmeyen denetimleri kaldırabilir. + ## Şifrelenmiş v2 alt ajan görevleri Codex v2 alt ajanları için önemli bir sınırlama vardır ([sorun @@ -373,6 +379,7 @@ saklanır: | `strategy` | Hayır | `"failover"` | İzin verilen değerler: `"failover"`, `"round-robin"`, `"random"`, `"least-used"`, `"reset-window"`. | | `stickyLimit` | Hayır | `1` | Yalnızca `round-robin` için geçerlidir; seçim başına 1 ile 100 arasında başarılı istek tam sayısı. | | `defaultEffort` | Hayır | `null` | `low`, `medium`, `high`, `xhigh`, `max` veya `ultra`; yalnızca arayan çabayı atladığında ve hedef desteği bildirdiğinde uygulanır. | +| `reasoningEffortMode` | Hayır | `"strict"` | `strict` veya `adaptive`; karma yetenek kesişimini ve hedefe özel normalizasyonu seçer. | | `alias` | Hayır | yok | İsteğe bağlı kırpılmış genel model kimliği; yukarıdaki takma ad kurallarını kullanın. Boş bir değer takma ad yok olarak saklanır. | | `nativeAlias` | Hayır | `false` | Şu anda desteklenen yalın bir yerel `alias`'ın yönlendirme ve katalog önceliği almasına açıkça izin verin. Asla takma addan çıkarılmaz. | | `displayName` | Hayır | yok | Sınırlı salt görüntüleme katalog etiketi. `nativeAlias` true olduğunda gerekli ve boş değildir. | diff --git a/docs-site/src/content/docs/zh-cn/guides/combos.md b/docs-site/src/content/docs/zh-cn/guides/combos.md index abe32ae786..ffd670b57c 100644 --- a/docs-site/src/content/docs/zh-cn/guides/combos.md +++ b/docs-site/src/content/docs/zh-cn/guides/combos.md @@ -175,6 +175,12 @@ combo 失败分为 **跳转** 失败和 **终止** 失败。 当目标能力未知,或者不包含配置的 effort 时,opencodex 会省略默认值,并保持目标自身行为不变。支持的值是 `low`、`medium`、`high`、`xhigh`、`max` 和 `ultra`;省略该字段或将其设为 `null`,就会把 effort 完全交给调用方和目标。 +## 混合 reasoning 能力 + +`reasoningEffortMode` 默认为 `"strict"`,发布所有目标 effort 列表的交集,包括显式空列表。`"adaptive"` 在计算交集时排除空列表,让混合 combo 保留选择器。未知列表在两种模式下都不限制目录交集。 + +发送时,显式空列表在两种模式下都会移除 effort 和 thinking 控制;未知列表仅在 adaptive 下移除这些控制。`reasoning.summary` 和其他非 effort 字段保持不变,已知非空目标继续按现有规则解析 effort。strict 的未知目标及普通 native Chat 的未知声明保留调用方控制。默认值填充不会覆盖现有 effort,但能力归一化可移除不支持的控制。 + ## 图片 / 多模态能力 默认情况下,combo 会发布其目标 **input modalities 的交集**(只有当每个目标都声明支持图片时,图片才会启用)。设置 `imageInput: "disabled"` 可在目标均支持图片时仍强制仅文本——目录会从 `inputModalities` 中去掉 `image`,带图请求会在分发前以 HTTP 400 拒绝。`"auto"`(或省略该字段)保持自动交集。 @@ -266,6 +272,7 @@ combo 会存储在顶层的 `combos` 对象中,并以 combo id 作为键: | `cooldownMs` | 否 | 未设置 → 上游回退值(请求速率限制代码为 `1302`/`1305` 的 429 为 5 秒,否则为 60 秒) | 1 到 600000 的整数。设置后,只要没有可用的上游 `Retry-After` 或 Codex 重置信号,就会作为每个目标的冷却时间应用,包括请求速率限制 429;未设置时使用上游回退值。 | | `waitForCooldownMs` | 否 | `0` | 0 到 600000 的整数。在返回 `combo_unavailable` 前等待最早恢复资格的冷却中目标的最长时间;请求中止会取消等待。 | | `defaultEffort` | 否 | `null` | `low`、`medium`、`high`、`xhigh`、`max` 或 `ultra`;仅当调用方省略 effort 且目标声明支持时才会应用。 | +| `reasoningEffortMode` | 否 | `"strict"` | `strict` 或 `adaptive`;选择混合能力交集和目标级控制归一化。 | | `imageInput` | 否 | `"auto"` | `"auto"` 或 `"disabled"`。`"auto"` 仅在每个目标都支持图片时发布图片能力;`"disabled"` 强制仅文本(从对外能力中去掉图片,并在分发前拒绝带图请求)。 | | `alias` | 否 | 无 | 可选的、已修剪的公开模型 id;使用上面的别名规则。空值会以“无别名”形式存储。 | | `nativeAlias` | 否 | `false` | 显式允许当前受支持的裸原生 alias 接管路由和 catalog 优先级;绝不会根据 alias 自动推断。 | diff --git a/docs-site/src/content/docs/zh-tw/guides/combos.md b/docs-site/src/content/docs/zh-tw/guides/combos.md index ce3ad70a94..10b30e4c28 100644 --- a/docs-site/src/content/docs/zh-tw/guides/combos.md +++ b/docs-site/src/content/docs/zh-tw/guides/combos.md @@ -187,6 +187,12 @@ Failover 是刻意受限的。它有助於目標特定的可用性、認證、 當目標能力未知或不包含設定的 effort 時,opencodex 省略預設值並保持目標自身行為不變。支援的值為 `low`、`medium`、`high`、`xhigh`、`max` 與 `ultra`;省略欄位或設為 `null` 可將 effort 完全交給呼叫者與目標。 +## 混合 reasoning 能力 + +`reasoningEffortMode` 預設為 `"strict"`,發布所有目標 effort 清單的交集,包括明確空清單。`"adaptive"` 計算交集時排除空清單,讓混合 combo 保留選擇器。未知清單在兩種模式下都不限制目錄交集。 + +傳送時,明確空清單在兩種模式下都會移除 effort 與 thinking 控制;未知清單只在 adaptive 移除這些控制。`reasoning.summary` 與其他非 effort 欄位保持不變,已知非空目標仍按現有規則解析 effort。strict 的未知目標及一般 native Chat 的未知宣告保留呼叫者控制。預設值補入不會覆寫現有 effort,但能力正規化可移除不支援的控制。 + ## 加密的 v2 子代理任務 Codex v2 子代理有一個重要限制([issue #92](https://github.com/lidge-jun/opencodex/issues/92))。原生父代只能將新生成 worker 的任務以為原生 ChatGPT 後端鑄造的密文發送。外部供應商無法讀取該 payload。 @@ -269,6 +275,7 @@ Combo 儲存於頂層 `combos` 物件中,以 combo id 為 key: | `strategy` | 否 | `"failover"` | 可用值為 `"failover"`、`"round-robin"`、`"random"`、`"least-used"`、`"reset-window"`。 | | `stickyLimit` | 否 | `1` | 僅適用於 `round-robin`:每次選擇的成功請求數,1 到 100 的整數。 | | `defaultEffort` | 否 | `null` | `low`、`medium`、`high`、`xhigh`、`max` 或 `ultra`;僅在呼叫者省略 effort 且目標宣告支援時套用。 | +| `reasoningEffortMode` | 否 | `"strict"` | `strict` 或 `adaptive`;選擇混合能力交集及目標層級控制正規化。 | | `alias` | 否 | 無 | 可選的修剪後公開模型 id;使用上述別名規則。空值儲存為無別名。 | ## 疑難排解 diff --git a/tests/server/server-combo-failover-e2e.test.ts b/tests/server/server-combo-failover-e2e.test.ts index 76b90720ba..4fe9381698 100644 --- a/tests/server/server-combo-failover-e2e.test.ts +++ b/tests/server/server-combo-failover-e2e.test.ts @@ -1236,6 +1236,34 @@ describe("server combo failover 030 activation matrix", () => { expect(bodies[2]!.reasoning_effort).toBe("xhigh"); }); + test("adaptive Responses combo preserves summary after removing unsupported controls", async () => { + const bodies: Array> = []; + const upstream = serve(async request => { + bodies.push(await request.json() as Record); + return Response.json(responsesSuccess("normalized", "m1")); + }); + for (const reasoningEfforts of [undefined, [], ["high", "xhigh"]]) { + const response = await post(comboConfig({ + a: provider("openai-responses", baseUrl(upstream), "key-a", { + ...(reasoningEfforts === undefined ? {} : { reasoningEfforts }), + modelSupportsReasoningSummaries: { m1: true }, + }), + }, undefined, { reasoningEffortMode: "adaptive" }), { + reasoning: { effort: "xhigh", summary: "concise" }, + reasoning_effort: "xhigh", thinking_budget: 8192, thinking: { type: "enabled" }, + }); + expect(response.status).toBe(200); + } + expect(bodies).toHaveLength(3); + for (const body of bodies.slice(0, 2)) { + expect(body.reasoning).toEqual({ summary: "concise" }); + expect(body).not.toHaveProperty("reasoning_effort"); + expect(body).not.toHaveProperty("thinking_budget"); + expect(body).not.toHaveProperty("thinking"); + } + expect(bodies[2]!.reasoning).toMatchObject({ effort: "xhigh", summary: "concise" }); + }); + test("all-target exhaustion promotes the final attempt reasoning wire to the logical row", async () => { const a = serve(() => Response.json({ error: { message: "first overloaded" } }, { status: 503 })); const b = serve(() => Response.json({ error: { message: "last overloaded" } }, { status: 503 })); From 37fb3812141fc2f9d8d93e4fa15d3aae65306ebd Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 21:17:15 +0900 Subject: [PATCH 4/5] docs(combos): document supported-rung default fallback across locales --- .../src/content/docs/fr/guides/combos.md | 14 ++----------- .../fr/reference/configuration/routing.md | 2 +- docs-site/src/content/docs/guides/combos.md | 14 ++----------- .../src/content/docs/ja/guides/combos.md | 9 ++------- .../ja/reference/configuration/routing.md | 2 +- .../src/content/docs/ko/guides/combos.md | 9 ++------- .../ko/reference/configuration/routing.md | 2 +- .../docs/reference/configuration/routing.md | 2 +- .../src/content/docs/ru/guides/combos.md | 14 ++----------- .../ru/reference/configuration/routing.md | 2 +- .../src/content/docs/tr/guides/combos.md | 20 ++++--------------- .../tr/reference/configuration/routing.md | 2 +- .../src/content/docs/zh-cn/guides/combos.md | 9 ++------- .../zh-cn/reference/configuration/routing.md | 2 +- .../src/content/docs/zh-tw/guides/combos.md | 9 ++------- .../zh-tw/reference/configuration/routing.md | 2 +- structure/transports/inventory.md | 2 +- 17 files changed, 27 insertions(+), 89 deletions(-) diff --git a/docs-site/src/content/docs/fr/guides/combos.md b/docs-site/src/content/docs/fr/guides/combos.md index 5120925d6d..dc18f68971 100644 --- a/docs-site/src/content/docs/fr/guides/combos.md +++ b/docs-site/src/content/docs/fr/guides/combos.md @@ -218,20 +218,10 @@ Le basculement est intentionnellement limité. Il facilite la disponibilité, l' ## Effort de raisonnement par défaut -`defaultEffort` fournit `reasoning.effort` uniquement lorsque toutes ces conditions sont vraies : +`defaultEffort` complète un `reasoning.effort` absent si le combo possède une valeur par défaut non nulle et si la liste des niveaux acceptés par la cible est connue et non vide. La valeur configurée est conservée si elle est acceptée ; sinon, le niveau accepté le plus élevé ne la dépassant pas est choisi, ou le niveau le plus bas si aucun n’est inférieur. Une liste inconnue ou vide n’ajoute aucune valeur par défaut. -1. le combo a un défaut non nul ; -2. l'appelant n'a pas fait d'effort ; et -3. le catalogue de la cible sélectionnée annonce cet effort précis. +Cette étape conserve un effort existant et les autres champs reasoning. La normalisation des capacités ci-dessous peut supprimer séparément les paramètres effort/thinking non acceptés. Valeurs possibles : `low`, `medium`, `high`, `xhigh`, `max`, `ultra` ; l’absence du champ ou `null` désactive l’ajout. -Si la requête n'a pas d'objet `reasoning`, opencodex en crée un. Si `reasoning` existe sans -`effort`, il préserve les autres champs et ajoute la valeur par défaut. Un effort fourni par l’appelant n’est -jamais écrasé. - -Lorsque la capacité cible est inconnue ou n'inclut pas l'effort configuré, opencodex omet le -par défaut et laisse le comportement de la cible inchangé. Les valeurs prises en charge sont `low`, `medium`, -`high`, `xhigh`, `max` et `ultra` ; omettez le champ ou réglez-le sur `null` pour laisser l'effort entièrement à -l'appelant et la cible. ## Capacités reasoning mixtes diff --git a/docs-site/src/content/docs/fr/reference/configuration/routing.md b/docs-site/src/content/docs/fr/reference/configuration/routing.md index 110d2fcd66..d1a0107adc 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/routing.md +++ b/docs-site/src/content/docs/fr/reference/configuration/routing.md @@ -58,7 +58,7 @@ Chaque clé de combinaison est un identifiant conforme à `[A-Za-z0-9][A-Za-z0-9 | `targets` | `{ provider: string; model: string; weight?: number }[]` | requis | Routes concrètes ordonnées. `weight` est compris entre 1 et 10000 et vaut `1` par défaut. | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | Stratégie de sélection. L’ordre des cibles définit la priorité de `failover` ; les poids déterminent les sélections de `round-robin` et de `random` ; `least-used` suit les réussites enregistrées ; `reset-window` suit la réinitialisation de quota la plus proche. | | `stickyLimit?` | `number` | `1` | Nombre de requêtes réussies conservées dans un même lot de rotation. Plage de 1 à 100. | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | non défini | Appliqué uniquement lorsque l’appelant ne précise aucun effort et que la cible sélectionnée annonce le niveau demandé. | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | non défini | `defaultEffort` complète un `reasoning.effort` absent si le combo possède une valeur par défaut non nulle et si la liste des niveaux acceptés par la cible est connue et non vide. La valeur configurée est conservée si elle est acceptée ; sinon, le niveau accepté le plus élevé ne la dépassant pas est choisi, ou le niveau le plus bas si aucun n’est inférieur. Une liste inconnue ou vide n’ajoute aucune valeur par défaut. | | `imageInput?` | `"auto" \| "disabled"` | `"auto"` | `"auto"` publie les images uniquement lorsque toutes les cibles les prennent en charge ; `"disabled"` impose le texte seul, retire les images des modalités publiées et rejette les requêtes qui en contiennent avant leur distribution. | | `alias?` | `string` | — | Identifiant public facultatif du modèle, à la place du slug canonique du sélecteur. | | `nativeAlias?` | `boolean` | `false` | Permet à un identifiant natif non qualifié actuellement pris en charge de prendre la priorité uniquement pour cet identifiant. Les identifiants non qualifiés `gpt-5.6-*` utilisent les identifiants Codex Pool/Direct. Les routes qualifiées par un compte restent distinctes. Les routes qualifiées par un fournisseur, telles que `openai-apikey/gpt-5.6-*`, utilisent la route configurée avec sa clé d’API et ne passent jamais par l’alias natif. | diff --git a/docs-site/src/content/docs/guides/combos.md b/docs-site/src/content/docs/guides/combos.md index c497ec918f..979a84ffcd 100644 --- a/docs-site/src/content/docs/guides/combos.md +++ b/docs-site/src/content/docs/guides/combos.md @@ -256,20 +256,10 @@ instead of growing memory without a bound. ## Default reasoning effort -`defaultEffort` supplies `reasoning.effort` only when all of these are true: +`defaultEffort` fills an absent `reasoning.effort` when the combo has a non-null default and the selected target has a known, nonempty supported ladder. If the target supports the configured value, it is retained; otherwise the highest supported rung at or below it is used, or the lowest supported rung when none is lower. Unknown or empty ladders omit the default. -1. the combo has a non-null default; -2. the caller did not set an effort; and -3. the selected target's catalog advertises that exact effort. +The default-injection step preserves existing effort and other reasoning fields. Capability normalization can separately remove unsupported effort/thinking controls as described below. Supported defaults are `low`, `medium`, `high`, `xhigh`, `max`, and `ultra`; omit the field or use `null` to disable default injection. -If the request has no `reasoning` object, opencodex creates one. If `reasoning` exists without an -`effort` property, it preserves the other fields and adds the default. A caller-provided effort is -never overwritten. - -When target capability is unknown or does not include the configured effort, opencodex omits the -default and leaves the target's own behavior unchanged. Supported values are `low`, `medium`, -`high`, `xhigh`, `max`, and `ultra`; omit the field or set it to `null` to leave effort entirely to -the caller and target. ### Mixed-capability groups (`reasoningEffortMode`) diff --git a/docs-site/src/content/docs/ja/guides/combos.md b/docs-site/src/content/docs/ja/guides/combos.md index efb2fa6021..7b2c62a9ec 100644 --- a/docs-site/src/content/docs/ja/guides/combos.md +++ b/docs-site/src/content/docs/ja/guides/combos.md @@ -139,15 +139,10 @@ ocx combo set balanced \ ## デフォルトの推論負荷 -`defaultEffort` は、次のすべてが当てはまる場合にのみ `reasoning.effort` を提供します。 +`defaultEffort` は、コンボの既定値が null でなく、対象の対応リストが既知で空でない場合に、省略された `reasoning.effort` を補います。設定値に対応していればその値を使い、そうでなければ設定値以下で最も高い段階を選びます。それもなければ最も低い対応段階を使います。不明または空のリストでは既定値を省略します。 -1. コンボには null 以外のデフォルトがあります。 -2. 呼び出し側は努力を設定しませんでした。そして -3. 選択したターゲットのカタログは、その正確な取り組みを宣伝します。 +既定値の補完は既存の effort と他の reasoning フィールドを保持します。以下の capability 正規化は、別途、非対応の effort/thinking 制御を削除できます。設定可能な既定値は `low`、`medium`、`high`、`xhigh`、`max`、`ultra` です。省略または `null` で補完を無効にします。 -リクエストに `reasoning` オブジェクトがない場合、opencodex はオブジェクトを作成します。 `reasoning` が `effort` プロパティなしで存在する場合、他のフィールドは保持され、デフォルトが追加されます。呼び出し元が提供した努力は決し​​て上書きされません。 - -ターゲットの機能が不明な場合、または設定されたエフォートが含まれていない場合、opencodex はデフォルトを省略し、ターゲット自体の動作を変更しないままにします。サポートされている値は、`low`、`medium`、`high`、`xhigh`、`max`、および `ultra` です。このフィールドを省略するか、`null` に設定して、呼び出し元とターゲットに作業を完全に任せます。 ## 異なる reasoning capability の組み合わせ diff --git a/docs-site/src/content/docs/ja/reference/configuration/routing.md b/docs-site/src/content/docs/ja/reference/configuration/routing.md index 18b238c8bf..6cd6e81ab6 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ja/reference/configuration/routing.md @@ -69,7 +69,7 @@ picker catalog の convergence だけが保留中で routing change は失われ | `targets` | `{ provider: string; model: string; weight?: number }[]` |必須 |具体的なルートを指示しました。 `weight` は 1 ~ 10000 で、デフォルトは `1` です。 | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` |選択戦略。ターゲットの順序は `failover` の優先順位となり、`weight` は `round-robin` と `random` の抽選に影響し、`least-used` は記録された成功数に従い、`reset-window` は最も早いクォータリセットに従います。 | | `stickyLimit?` | `number` | `1` |成功したリクエストは 1 つのラウンドロビン バッチに保持されます。範囲は 1 ~ 100。 | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` |設定を解除する |呼び出し元が努力を省略し、選択されたターゲットが要求されたラングをアドバタイズする場合にのみ適用されます。 | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` |設定を解除する | `defaultEffort` は、コンボの既定値が null でなく、対象の対応リストが既知で空でない場合に、省略された `reasoning.effort` を補います。設定値に対応していればその値を使い、そうでなければ設定値以下で最も高い段階を選びます。それもなければ最も低い対応段階を使います。不明または空のリストでは既定値を省略します。 | | `alias?` | `string` | — |正規のピッカー スラグの代わりのオプションのパブリック モデル ID。 | | `nativeAlias?` | `boolean` | `false` | 現在サポートされている bare native id に限り、その未修飾 id で優先します。アカウント修飾およびプロバイダー修飾の OpenAI ルートは別のままです。 | | `displayName?` | `string` | — | catalog 表示専用ラベル。native alias では空でない値が必須です。 | diff --git a/docs-site/src/content/docs/ko/guides/combos.md b/docs-site/src/content/docs/ko/guides/combos.md index b7f9165ebb..e507889c6f 100644 --- a/docs-site/src/content/docs/ko/guides/combos.md +++ b/docs-site/src/content/docs/ko/guides/combos.md @@ -145,15 +145,10 @@ ocx combo set balanced \ ## 기본 reasoning effort -`defaultEffort`는 다음 조건이 모두 참일 때만 `reasoning.effort`를 채웁니다. +`defaultEffort`는 콤보 기본값이 null이 아니고, 선택한 대상의 지원 목록이 알려져 있으며 비어 있지 않을 때 생략된 `reasoning.effort`를 채웁니다. 설정값을 지원하면 그대로 사용합니다. 그렇지 않으면 설정값 이하의 가장 높은 지원 단계를 사용하고, 그런 단계가 없으면 가장 낮은 지원 단계를 사용합니다. 지원 목록이 없거나 비어 있으면 기본값을 생략합니다. -1. 콤보에 null이 아닌 기본값이 있습니다. -2. 호출자가 effort를 설정하지 않았습니다. -3. 선택된 대상의 카탈로그가 그 정확한 effort를 광고합니다. +기본값 주입은 기존 effort와 다른 reasoning 필드를 보존합니다. 아래의 capability 정규화는 별도로 지원되지 않는 effort·thinking 제어를 제거할 수 있습니다. 기본값은 `low`, `medium`, `high`, `xhigh`, `max`, `ultra`이며, 필드를 생략하거나 `null`로 설정하면 주입하지 않습니다. -요청에 `reasoning` 객체가 없으면 opencodex가 새로 만듭니다. `reasoning`은 있지만 `effort` 속성이 없으면 다른 필드는 그대로 두고 기본값만 추가합니다. 호출자가 준 effort는 절대 덮어쓰지 않습니다. - -대상 기능을 알 수 없거나 설정한 effort를 포함하지 않으면 opencodex는 기본값을 생략하고 대상의 동작은 그대로 둡니다. 지원 값은 `low`, `medium`, `high`, `xhigh`, `max`, `ultra`입니다. effort를 호출자와 대상에 완전히 맡기려면 이 필드를 생략하거나 `null`로 설정하십시오. ## 서로 다른 reasoning capability diff --git a/docs-site/src/content/docs/ko/reference/configuration/routing.md b/docs-site/src/content/docs/ko/reference/configuration/routing.md index cfc20d07fc..1a25960344 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ko/reference/configuration/routing.md @@ -68,7 +68,7 @@ Codex Auth 페이지에서 이 picker 동작을 opt-in할 수 있습니다. 비 | `targets` | `{ provider: string; model: string; weight?: number }[]` | required | 순서가 있는 concrete route입니다. `weight`는 1–10000이며 기본값은 `1`입니다. | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | 선택 전략입니다. 대상 순서는 `failover` 우선순위이고, 가중치는 `round-robin`과 `random` 추첨 비율을 결정하며, `least-used`는 기록된 성공 횟수를 따르고, `reset-window`는 가장 가까운 할당량 재설정을 따릅니다. | | `stickyLimit?` | `number` | `1` | 한 round-robin 배치에서 유지되는 성공 요청 수입니다. 범위는 1–100입니다. | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | 호출자가 effort를 생략했고 선택된 대상이 요청한 rung를 광고할 때만 적용됩니다. | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort`는 콤보 기본값이 null이 아니고, 선택한 대상의 지원 목록이 알려져 있으며 비어 있지 않을 때 생략된 `reasoning.effort`를 채웁니다. 설정값을 지원하면 그대로 사용합니다. 그렇지 않으면 설정값 이하의 가장 높은 지원 단계를 사용하고, 그런 단계가 없으면 가장 낮은 지원 단계를 사용합니다. 지원 목록이 없거나 비어 있으면 기본값을 생략합니다. | | `alias?` | `string` | — | 정규화된 picker slug 대신 쓰는 선택적 공개 model id입니다. | | `nativeAlias?` | `boolean` | `false` | 현재 지원되는 bare native id가 해당 비수식 id에만 우선하도록 합니다. 계정 또는 프로바이더로 수식된 OpenAI route는 별도로 유지됩니다. | | `displayName?` | `string` | — | catalog 표시 전용 label이며 native alias에서는 비어 있지 않아야 합니다. | diff --git a/docs-site/src/content/docs/reference/configuration/routing.md b/docs-site/src/content/docs/reference/configuration/routing.md index 25bd32f64e..7cd038b649 100644 --- a/docs-site/src/content/docs/reference/configuration/routing.md +++ b/docs-site/src/content/docs/reference/configuration/routing.md @@ -90,7 +90,7 @@ namespace, and cannot use reserved bare native families such as `gpt-*`, `o1-*`, | `stickyLimit?` | `number` | `1` | Successful requests retained in one round-robin batch. Range 1–100. Applies only to round-robin. | | `cooldownMs?` | `number` | unset → upstream fallback (5 s for request-rate 429 codes `1302`/`1305`, otherwise 60 s) | Range 1–600000. When set, applies whenever no usable upstream `Retry-After` or Codex reset signal exists, including request-rate 429s; when unset, uses the upstream fallback. Upstream signals take precedence and all cooldowns are capped at 10 minutes. | | `waitForCooldownMs?` | `number` | `0` | Maximum wait for the earliest eligible cooling target on each selection attempt before returning `combo_unavailable`. Range 0–600000; an abort cancels the wait. | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | Applied only when the caller omits effort and the selected target advertises the requested rung. | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort` fills an absent `reasoning.effort` when the combo has a non-null default and the selected target has a known, nonempty supported ladder. If the target supports the configured value, it is retained; otherwise the highest supported rung at or below it is used, or the lowest supported rung when none is lower. Unknown or empty ladders omit the default. | | `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` intersects every known target effort ladder, so a target advertising no effort control empties the combo's picker. `"adaptive"` excludes those empty ladders from the published intersection. Picker metadata only; target selection and dispatch are unchanged. | | `imageInput?` | `"auto" \| "disabled"` | `"auto"` | `"auto"` publishes image only when every target supports images; `"disabled"` forces text-only (drops image from published modalities and rejects image-bearing requests before dispatch). | | `alias?` | `string` | — | Optional public model id in place of the canonical picker slug. | diff --git a/docs-site/src/content/docs/ru/guides/combos.md b/docs-site/src/content/docs/ru/guides/combos.md index d54c03e33e..53a6b4b5f3 100644 --- a/docs-site/src/content/docs/ru/guides/combos.md +++ b/docs-site/src/content/docs/ru/guides/combos.md @@ -177,20 +177,10 @@ Failover намеренно ограничен. Он помогает при п ## Effort по умолчанию -`defaultEffort` подставляет `reasoning.effort` только если одновременно выполняются все условия: +`defaultEffort` заполняет отсутствующий `reasoning.effort`, если задан ненулевой default и список поддерживаемых уровней цели известен и непуст. Поддерживаемое настроенное значение сохраняется; иначе выбирается максимальный поддерживаемый уровень не выше него, а если такого нет — минимальный поддерживаемый уровень. При неизвестном или пустом списке default не добавляется. -1. у combo задано ненулевое значение по умолчанию; -2. вызывающая сторона сама не указала effort; и -3. каталог выбранной цели объявляет поддержку именно этого effort. +Подстановка сохраняет существующий effort и остальные поля reasoning. Нормализация возможностей ниже может отдельно удалить неподдерживаемые параметры effort/thinking. Значения default: `low`, `medium`, `high`, `xhigh`, `max`, `ultra`; отсутствие поля или `null` отключает подстановку. -Если в запросе нет объекта `reasoning`, opencodex создаёт его. Если `reasoning` есть, но в нём нет -свойства `effort`, остальные поля сохраняются, а значение по умолчанию добавляется. Effort, -заданный вызывающей стороной, никогда не перезаписывается. - -Если возможности цели неизвестны или не включают настроенный effort, opencodex опускает значение -по умолчанию и оставляет нативное поведение цели без изменений. Поддерживаются `low`, `medium`, -`high`, `xhigh`, `max` и `ultra`; опустите поле или задайте `null`, чтобы полностью оставить выбор -effort вызывающей стороне и цели. ## Разные возможности reasoning в одном combo diff --git a/docs-site/src/content/docs/ru/reference/configuration/routing.md b/docs-site/src/content/docs/ru/reference/configuration/routing.md index dbc7181044..029df5594e 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ru/reference/configuration/routing.md @@ -87,7 +87,7 @@ selector-qualified строки и возвращает обычные GPT-ст | `targets` | `{ provider: string; model: string; weight?: number }[]` | required | Упорядоченные конкретные маршруты. `weight` находится в диапазоне 1–10000 и по умолчанию равен `1`. | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | Стратегия выбора. Порядок целей задаёт приоритет `failover`; значения `weight` определяют взвешивание выборов `round-robin` и `random`; `least-used` следует числу зарегистрированных успешных запросов; `reset-window` следует ближайшему сбросу квоты. | | `stickyLimit?` | `number` | `1` | Число успешных запросов, удерживаемых в одной партии round-robin. Диапазон 1–100. | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | Применяется, только если вызывающая сторона не задала effort, а выбранная цель объявляет эту ступень. | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort` заполняет отсутствующий `reasoning.effort`, если задан ненулевой default и список поддерживаемых уровней цели известен и непуст. Поддерживаемое настроенное значение сохраняется; иначе выбирается максимальный поддерживаемый уровень не выше него, а если такого нет — минимальный поддерживаемый уровень. При неизвестном или пустом списке default не добавляется. | | `alias?` | `string` | — | Необязательный публичный id модели вместо канонического slug в селекторе. | | `nativeAlias?` | `boolean` | `false` | Даёт поддерживаемому bare native id приоритет только для этого неквалифицированного id. Bare `gpt-5.6-*` использует учётные данные Codex Pool/Direct. Маршруты с квалификатором аккаунта остаются отдельными. Провайдер-квалифицированные маршруты, например `openai-apikey/gpt-5.6-*`, используют настроенный API-ключ и никогда не переходят на native alias. | | `displayName?` | `string` | — | Метка только для catalog; для native alias обязательна и не может быть пустой. | diff --git a/docs-site/src/content/docs/tr/guides/combos.md b/docs-site/src/content/docs/tr/guides/combos.md index ddb877cbcb..4ac9e0febf 100644 --- a/docs-site/src/content/docs/tr/guides/combos.md +++ b/docs-site/src/content/docs/tr/guides/combos.md @@ -249,22 +249,10 @@ veya politika retlerini gizlemez. ## Varsayılan akıl yürütme çabası -`defaultEffort`, yalnızca bunların tümü doğru olduğunda `reasoning.effort` -sağlar: - -1. kombonun boş olmayan (non-null) bir varsayılanı vardır; -2. arayan bir çaba ayarlamamıştır; ve -3. seçilen hedefin kataloğu tam olarak bu çabayı bildirmektedir. - -İstekte bir `reasoning` nesnesi yoksa opencodex bir tane oluşturur. Bir `effort` -özelliği olmadan `reasoning` varsa diğer alanları korur ve varsayılanı ekler. -Arayan tarafından sağlanan bir çabanın üzerine asla yazılmaz. - -Hedef yeteneği bilinmediğinde veya yapılandırılan çabayı içermediğinde opencodex -varsayılanı atlar ve hedefin kendi davranışını değiştirmeden bırakır. -Desteklenen değerler `low`, `medium`, `high`, `xhigh`, `max` ve `ultra`'dır; -çabayı tamamen arayana ve hedefe bırakmak için alanı atlayın veya `null` olarak -ayarlayın. +`defaultEffort`, combo varsayılanı null değilse ve hedefin desteklenen seviye listesi bilinen ve boş olmayan bir listeyse eksik `reasoning.effort` değerini doldurur. Yapılandırılmış değer destekleniyorsa korunur; değilse bu değeri aşmayan en yüksek desteklenen seviye, böyle bir seviye yoksa en düşük desteklenen seviye kullanılır. Liste bilinmiyor veya boşsa varsayılan eklenmez. + +Varsayılan ekleme mevcut effort ve diğer reasoning alanlarını korur. Aşağıdaki yetenek normalizasyonu desteklenmeyen effort/thinking denetimlerini ayrıca kaldırabilir. Desteklenen varsayılanlar: `low`, `medium`, `high`, `xhigh`, `max`, `ultra`; alanı atlamak veya `null` kullanmak eklemeyi kapatır. + ## Farklı reasoning yetenekleri diff --git a/docs-site/src/content/docs/tr/reference/configuration/routing.md b/docs-site/src/content/docs/tr/reference/configuration/routing.md index 74d239ee14..b81f1464cd 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/routing.md +++ b/docs-site/src/content/docs/tr/reference/configuration/routing.md @@ -110,7 +110,7 @@ aileleri kullanamaz. | `targets` | `{ provider: string; model: string; weight?: number }[]` | gerekli | Sıralı somut rotalar. `weight` 1–10000 arasındadır ve varsayılan olarak `1`'dir. | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | Seçim stratejisi. Hedef sırası `failover` önceliğini belirler; `weight` değerleri `round-robin` ve `random` seçimlerini biçimlendirir; `least-used` kaydedilen başarılı istekleri izler; `reset-window` en yakın kota sıfırlamasını izler. | | `stickyLimit?` | `number` | `1` | Tek bir round-robin grubunda tutulan başarılı istekler. Aralık 1–100. | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | ayarlanmamış | Yalnızca arayan çabayı atladığında ve seçilen hedef istenen basamağı bildirdiğinde uygulanır. | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | ayarlanmamış | `defaultEffort`, combo varsayılanı null değilse ve hedefin desteklenen seviye listesi bilinen ve boş olmayan bir listeyse eksik `reasoning.effort` değerini doldurur. Yapılandırılmış değer destekleniyorsa korunur; değilse bu değeri aşmayan en yüksek desteklenen seviye, böyle bir seviye yoksa en düşük desteklenen seviye kullanılır. Liste bilinmiyor veya boşsa varsayılan eklenmez. | | `alias?` | `string` | — | Kurallı seçici slug'ı yerine isteğe bağlı genel model kimliği. | | `nativeAlias?` | `boolean` | `false` | Şu anda desteklenen bir yalın yerel kimliğin yalnızca o niteliksiz kimlik için öncelikli olmasına izin verin. Yalın `gpt-5.6-*` kimlikleri Codex Havuz/Direct kimlik bilgilerini kullanır. Hesap nitelikli rotalar ayrı kalır. `openai-apikey/gpt-5.6-*` gibi sağlayıcı nitelikli rotalar yapılandırılmış API anahtarı rotalarını kullanır ve asla yerel takma ada düşmez. | | `displayName?` | `string` | — | Yalnızca görüntüleme amaçlı katalog etiketi, yerel bir takma ad için gerekli ve boş olmamalıdır. | diff --git a/docs-site/src/content/docs/zh-cn/guides/combos.md b/docs-site/src/content/docs/zh-cn/guides/combos.md index ffd670b57c..7c8c8efd63 100644 --- a/docs-site/src/content/docs/zh-cn/guides/combos.md +++ b/docs-site/src/content/docs/zh-cn/guides/combos.md @@ -165,15 +165,10 @@ combo 失败分为 **跳转** 失败和 **终止** 失败。 ## 默认推理力度 -只有在以下所有条件都满足时,`defaultEffort` 才会提供 `reasoning.effort`: +当 combo 配置了非 null 默认值且目标支持列表已知且非空时,`defaultEffort` 会填充省略的 `reasoning.effort`。目标支持配置值时保留该值,否则选择不高于配置值的最高支持档位;若不存在更低档位,则使用最低支持档位。未知或空列表不会注入默认值。 -1. combo 有一个非空默认值; -2. 调用方没有设置 effort;并且 -3. 选中的目标目录明确声明了该精确的 effort。 +默认值注入保留已有 effort 和其他 reasoning 字段。下述能力归一化可单独移除不支持的 effort/thinking 控制。默认值支持 `low`、`medium`、`high`、`xhigh`、`max`、`ultra`;省略字段或设为 `null` 可关闭注入。 -如果请求没有 `reasoning` 对象,opencodex 会创建一个。如果 `reasoning` 存在但没有 `effort` 属性,它会保留其他字段并添加默认值。调用方提供的 effort 永远不会被覆盖。 - -当目标能力未知,或者不包含配置的 effort 时,opencodex 会省略默认值,并保持目标自身行为不变。支持的值是 `low`、`medium`、`high`、`xhigh`、`max` 和 `ultra`;省略该字段或将其设为 `null`,就会把 effort 完全交给调用方和目标。 ## 混合 reasoning 能力 diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md b/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md index 609c9ab48f..6232bc0306 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md @@ -73,7 +73,7 @@ Codex Auth 页面将此 picker 行为作为选择加入项。关闭它会隐藏 | `targets` | `{ provider: string; model: string; weight?: number }[]` | required | 有序的具体路由。`weight` 范围为 1–10000,默认值为 `1`。 | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | 选择策略。目标顺序表示 `failover` 优先级;`weight` 决定 `round-robin` 和 `random` 的抽取权重;`least-used` 根据记录的成功次数选择;`reset-window` 跟随最近的额度重置。 | | `stickyLimit?` | `number` | `1` | 在单个轮询批次中保留的成功请求数。范围 1–100。 | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | 仅在调用方省略 effort 且所选目标声明了请求的档位时应用。 | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | 当 combo 配置了非 null 默认值且目标支持列表已知且非空时,`defaultEffort` 会填充省略的 `reasoning.effort`。目标支持配置值时保留该值,否则选择不高于配置值的最高支持档位;若不存在更低档位,则使用最低支持档位。未知或空列表不会注入默认值。 | | `imageInput?` | `"auto" \| "disabled"` | `"auto"` | `"auto"` 仅在每个目标都支持图片时发布图片能力;`"disabled"` 强制仅文本(从对外能力中去掉图片,并在分发前拒绝带图请求)。 | | `alias?` | `string` | — | 可选的公开 model id,用于替代规范化的选择器 slug。 | | `nativeAlias?` | `boolean` | `false` | 仅让当前受支持的裸原生 id 对该不带限定前缀的 id 优先;带账号或提供方限定的 OpenAI 路由仍是独立路由。 | diff --git a/docs-site/src/content/docs/zh-tw/guides/combos.md b/docs-site/src/content/docs/zh-tw/guides/combos.md index 10b30e4c28..be566c77cd 100644 --- a/docs-site/src/content/docs/zh-tw/guides/combos.md +++ b/docs-site/src/content/docs/zh-tw/guides/combos.md @@ -177,15 +177,10 @@ Failover 是刻意受限的。它有助於目標特定的可用性、認證、 ## 預設推理 effort -`defaultEffort` 僅在以下全為真時提供 `reasoning.effort`: +當 combo 設定非 null 預設值且目標支援清單已知且非空時,`defaultEffort` 會補入省略的 `reasoning.effort`。目標支援設定值時保留該值,否則選擇不高於設定值的最高支援層級;若沒有更低層級,則使用最低支援層級。未知或空清單不會注入預設值。 -1. combo 有非 null 預設值; -2. 呼叫者未設定 effort;且 -3. 所選目標的目錄宣告該精確 effort。 +預設值補入會保留既有 effort 與其他 reasoning 欄位。下述能力正規化可另外移除不支援的 effort/thinking 控制。預設值支援 `low`、`medium`、`high`、`xhigh`、`max`、`ultra`;省略欄位或設為 `null` 可關閉注入。 -若請求沒有 `reasoning` 物件,opencodex 建立一個。若 `reasoning` 存在但無 `effort` 屬性,它保留其他欄位並加入預設值。呼叫者提供的 effort 永不被覆寫。 - -當目標能力未知或不包含設定的 effort 時,opencodex 省略預設值並保持目標自身行為不變。支援的值為 `low`、`medium`、`high`、`xhigh`、`max` 與 `ultra`;省略欄位或設為 `null` 可將 effort 完全交給呼叫者與目標。 ## 混合 reasoning 能力 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md b/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md index 13a4e2622e..50ef13c16b 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md @@ -56,7 +56,7 @@ Codex Auth 頁面將此 picker 行為作為選擇加入功能暴露。停用它 | `targets` | `{ provider: string; model: string; weight?: number }[]` | 必填 | 有序的具體路由。`weight` 為 1–10000,預設 `1`。 | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | 選擇策略。目標順序為 `failover` 優先序;`weight` 塑造 `round-robin` 與 `random` 抽選;`least-used` 依循已記錄的成功次數;`reset-window` 依循最早的配額重設。 | | `stickyLimit?` | `number` | `1` | 在一個 round-robin 批次中保留的成功請求數。範圍 1–100。 | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | 未設定 | 僅在呼叫者省略 effort 且所選目標廣告請求的階層時套用。 | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | 未設定 | 當 combo 設定非 null 預設值且目標支援清單已知且非空時,`defaultEffort` 會補入省略的 `reasoning.effort`。目標支援設定值時保留該值,否則選擇不高於設定值的最高支援層級;若沒有更低層級,則使用最低支援層級。未知或空清單不會注入預設值。 | | `alias?` | `string` | — | 可選的公開模型 id,取代標準 picker slug。 | | `nativeAlias?` | `boolean` | `false` | 讓目前支援的裸原生 id 僅對該未限定 id 取得優先。裸 `gpt-5.6-*` id 使用 Codex 池/Direct 憑證。帳號限定路由保持獨立。供應商限定路由(如 `openai-apikey/gpt-5.6-*`)使用其設定的 API-key 路由,且永不會落到原生別名。 | | `displayName?` | `string` | — | 僅顯示的目錄標籤,對原生別名為必填且非空。 | diff --git a/structure/transports/inventory.md b/structure/transports/inventory.md index 60328ab2cb..e76cc4fb74 100644 --- a/structure/transports/inventory.md +++ b/structure/transports/inventory.md @@ -14,7 +14,7 @@ surface is listed here so a maintainer can find the owner without grepping: | Adapter execution support | `src/adapters/run-turn-queue.ts`, `src/adapters/tool-catalog-nudge.ts`, `src/adapters/identity.ts`, `src/adapters/image.ts`, `src/adapters/upstream-http-error.ts` | Shared machinery: turn ordering, tool-catalog nudging, client fingerprinting, image conversion, upstream error normalization. | | Cursor (beyond the sections above) | `src/adapters/cursor/live-transport.ts`, `src/adapters/cursor/http1-bidi.ts`, `src/adapters/cursor/live-models.ts`, `src/adapters/cursor/transport-retry.ts`, `src/adapters/cursor/mcp-manager.ts`, `src/adapters/cursor/thread-continuity.ts`, `src/adapters/cursor/checkpoint-store.ts` | Thread continuity is the point: a retry must not start a new Cursor thread, and a validated checkpoint must not rebuild the full root history. HTTP/2 remains the default; an explicit `http1.1`/`h1` pin maps the bidi run onto Cursor's `RunSSE` receive stream plus sequenced `BidiAppend` sends, and applies to live discovery too. | | Claude Messages | `src/server/claude-messages.ts` | Routed translation, a native Anthropic passthrough branch, and `count_tokens`. | -| Chat Completions inbound | `src/server/chat-completions.ts`, `src/chat/`, `src/adapters/openai-chat.ts` | Inbound translation onto the same routing pipeline. The content mapper preserves image URLs and supported detail, including screenshot-bearing tool results; target adapters own image placement on their wire. Image-free tool results stay strings. Native passthrough consults the existing model capability ladder before forwarding `reasoning_effort`. | +| Chat Completions inbound | `src/server/chat-completions.ts`, `src/server/chat-native.ts`, `src/chat/`, `src/adapters/openai-chat.ts` | Inbound translation onto the same routing pipeline. The content mapper preserves image URLs and supported detail, including screenshot-bearing tool results; target adapters own image placement on their wire. Image-free tool results stay strings. The native handler owns pin/cap normalization; the adapter wire builder removes effort only for explicit empty declarations or no-reasoning models, preserving unknown raw declarations. | | Hosted search relay | `src/server/search.ts` | Direct relay; distinct from the web-search sidecar loop below. | | Image/video generation loop | `src/images/loop.ts`, `src/images/plan.ts`, `src/images/fulfill.ts`, `src/images/xai-client.ts`, `src/images/xai-video-client.ts`, `src/images/artifacts.ts` | A provider-returned image URL is downloaded into a local artifact once, then served locally; warnings stay URL-free because provider CDN URLs may embed credentials. | | GitHub Copilot | `src/providers/xai-transport.ts` (`resolveProviderTransport`), `src/providers/github-copilot-transport.ts` | `resolveProviderTransport` selects the Copilot transport when the routed provider name is `github-copilot`; the Copilot module then resolves its headers and base URL, and the registry seeds the provider row and model fallback. | From 32121b4735871b4c8cfbc18c34176f5d3a7c6282 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 22:25:10 +0900 Subject: [PATCH 5/5] docs(combos): align localized routing mode and null semantics --- .../src/content/docs/fr/reference/configuration/routing.md | 1 + .../src/content/docs/ja/reference/configuration/routing.md | 1 + .../src/content/docs/ko/reference/configuration/routing.md | 1 + docs-site/src/content/docs/reference/configuration/routing.md | 2 +- docs-site/src/content/docs/ru/guides/combos.md | 2 +- .../src/content/docs/ru/reference/configuration/routing.md | 3 ++- .../src/content/docs/tr/reference/configuration/routing.md | 1 + .../src/content/docs/zh-cn/reference/configuration/routing.md | 1 + .../src/content/docs/zh-tw/reference/configuration/routing.md | 1 + 9 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs-site/src/content/docs/fr/reference/configuration/routing.md b/docs-site/src/content/docs/fr/reference/configuration/routing.md index d1a0107adc..64713d645c 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/routing.md +++ b/docs-site/src/content/docs/fr/reference/configuration/routing.md @@ -59,6 +59,7 @@ Chaque clé de combinaison est un identifiant conforme à `[A-Za-z0-9][A-Za-z0-9 | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | Stratégie de sélection. L’ordre des cibles définit la priorité de `failover` ; les poids déterminent les sélections de `round-robin` et de `random` ; `least-used` suit les réussites enregistrées ; `reset-window` suit la réinitialisation de quota la plus proche. | | `stickyLimit?` | `number` | `1` | Nombre de requêtes réussies conservées dans un même lot de rotation. Plage de 1 à 100. | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | non défini | `defaultEffort` complète un `reasoning.effort` absent si le combo possède une valeur par défaut non nulle et si la liste des niveaux acceptés par la cible est connue et non vide. La valeur configurée est conservée si elle est acceptée ; sinon, le niveau accepté le plus élevé ne la dépassant pas est choisi, ou le niveau le plus bas si aucun n’est inférieur. Une liste inconnue ou vide n’ajoute aucune valeur par défaut. | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` calcule l’intersection des listes connues, y compris les listes vides ; `"adaptive"` exclut les listes vides. Les listes inconnues ne limitent l’intersection dans aucun des deux modes. À l’envoi, les listes explicitement vides suppriment les paramètres effort/thinking dans les deux modes ; les listes inconnues les suppriment seulement en adaptive. `reasoning.summary` est conservé. La résolution des listes connues non vides ainsi que le choix et l’ordre des cibles restent inchangés. | | `imageInput?` | `"auto" \| "disabled"` | `"auto"` | `"auto"` publie les images uniquement lorsque toutes les cibles les prennent en charge ; `"disabled"` impose le texte seul, retire les images des modalités publiées et rejette les requêtes qui en contiennent avant leur distribution. | | `alias?` | `string` | — | Identifiant public facultatif du modèle, à la place du slug canonique du sélecteur. | | `nativeAlias?` | `boolean` | `false` | Permet à un identifiant natif non qualifié actuellement pris en charge de prendre la priorité uniquement pour cet identifiant. Les identifiants non qualifiés `gpt-5.6-*` utilisent les identifiants Codex Pool/Direct. Les routes qualifiées par un compte restent distinctes. Les routes qualifiées par un fournisseur, telles que `openai-apikey/gpt-5.6-*`, utilisent la route configurée avec sa clé d’API et ne passent jamais par l’alias natif. | diff --git a/docs-site/src/content/docs/ja/reference/configuration/routing.md b/docs-site/src/content/docs/ja/reference/configuration/routing.md index 6cd6e81ab6..c6a6d80772 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ja/reference/configuration/routing.md @@ -70,6 +70,7 @@ picker catalog の convergence だけが保留中で routing change は失われ | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` |選択戦略。ターゲットの順序は `failover` の優先順位となり、`weight` は `round-robin` と `random` の抽選に影響し、`least-used` は記録された成功数に従い、`reset-window` は最も早いクォータリセットに従います。 | | `stickyLimit?` | `number` | `1` |成功したリクエストは 1 つのラウンドロビン バッチに保持されます。範囲は 1 ~ 100。 | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` |設定を解除する | `defaultEffort` は、コンボの既定値が null でなく、対象の対応リストが既知で空でない場合に、省略された `reasoning.effort` を補います。設定値に対応していればその値を使い、そうでなければ設定値以下で最も高い段階を選びます。それもなければ最も低い対応段階を使います。不明または空のリストでは既定値を省略します。 | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` は空リストを含む既知の対応リストの共通部分を公開し、`"adaptive"` は空リストを除外します。不明なリストは両モードで共通部分を制限しません。送信時、明示的な空リストは両モードで effort/thinking 制御を削除し、不明なリストでは adaptive のみ削除します。`reasoning.summary` は保持されます。既知の空でない対象の effort 解決と対象の選択・順序は変わりません。 | | `alias?` | `string` | — |正規のピッカー スラグの代わりのオプションのパブリック モデル ID。 | | `nativeAlias?` | `boolean` | `false` | 現在サポートされている bare native id に限り、その未修飾 id で優先します。アカウント修飾およびプロバイダー修飾の OpenAI ルートは別のままです。 | | `displayName?` | `string` | — | catalog 表示専用ラベル。native alias では空でない値が必須です。 | diff --git a/docs-site/src/content/docs/ko/reference/configuration/routing.md b/docs-site/src/content/docs/ko/reference/configuration/routing.md index 1a25960344..2f617695ce 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ko/reference/configuration/routing.md @@ -69,6 +69,7 @@ Codex Auth 페이지에서 이 picker 동작을 opt-in할 수 있습니다. 비 | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | 선택 전략입니다. 대상 순서는 `failover` 우선순위이고, 가중치는 `round-robin`과 `random` 추첨 비율을 결정하며, `least-used`는 기록된 성공 횟수를 따르고, `reset-window`는 가장 가까운 할당량 재설정을 따릅니다. | | `stickyLimit?` | `number` | `1` | 한 round-robin 배치에서 유지되는 성공 요청 수입니다. 범위는 1–100입니다. | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort`는 콤보 기본값이 null이 아니고, 선택한 대상의 지원 목록이 알려져 있으며 비어 있지 않을 때 생략된 `reasoning.effort`를 채웁니다. 설정값을 지원하면 그대로 사용합니다. 그렇지 않으면 설정값 이하의 가장 높은 지원 단계를 사용하고, 그런 단계가 없으면 가장 낮은 지원 단계를 사용합니다. 지원 목록이 없거나 비어 있으면 기본값을 생략합니다. | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"`는 빈 목록을 포함한 알려진 대상 지원 목록의 교집합을 사용하고, `"adaptive"`는 빈 목록을 제외합니다. 알 수 없는 목록은 두 모드 모두 교집합을 제한하지 않습니다. 전송 시 명시적 빈 목록은 두 모드에서 effort·thinking 제어를 제거하고, 알 수 없는 목록은 adaptive에서만 제거합니다. `reasoning.summary`는 보존됩니다. 알려진 비어 있지 않은 대상의 effort 결정과 대상 선택·순서는 그대로입니다. | | `alias?` | `string` | — | 정규화된 picker slug 대신 쓰는 선택적 공개 model id입니다. | | `nativeAlias?` | `boolean` | `false` | 현재 지원되는 bare native id가 해당 비수식 id에만 우선하도록 합니다. 계정 또는 프로바이더로 수식된 OpenAI route는 별도로 유지됩니다. | | `displayName?` | `string` | — | catalog 표시 전용 label이며 native alias에서는 비어 있지 않아야 합니다. | diff --git a/docs-site/src/content/docs/reference/configuration/routing.md b/docs-site/src/content/docs/reference/configuration/routing.md index 7cd038b649..2934c337a6 100644 --- a/docs-site/src/content/docs/reference/configuration/routing.md +++ b/docs-site/src/content/docs/reference/configuration/routing.md @@ -91,7 +91,7 @@ namespace, and cannot use reserved bare native families such as `gpt-*`, `o1-*`, | `cooldownMs?` | `number` | unset → upstream fallback (5 s for request-rate 429 codes `1302`/`1305`, otherwise 60 s) | Range 1–600000. When set, applies whenever no usable upstream `Retry-After` or Codex reset signal exists, including request-rate 429s; when unset, uses the upstream fallback. Upstream signals take precedence and all cooldowns are capped at 10 minutes. | | `waitForCooldownMs?` | `number` | `0` | Maximum wait for the earliest eligible cooling target on each selection attempt before returning `combo_unavailable`. Range 0–600000; an abort cancels the wait. | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort` fills an absent `reasoning.effort` when the combo has a non-null default and the selected target has a known, nonempty supported ladder. If the target supports the configured value, it is retained; otherwise the highest supported rung at or below it is used, or the lowest supported rung when none is lower. Unknown or empty ladders omit the default. | -| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` intersects every known target effort ladder, so a target advertising no effort control empties the combo's picker. `"adaptive"` excludes those empty ladders from the published intersection. Picker metadata only; target selection and dispatch are unchanged. | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` intersects all known target ladders, including empty ones; `"adaptive"` excludes empty ladders. Unknown ladders are catalog wildcards in both modes. At dispatch, explicit empty ladders remove effort/thinking controls in both modes; unknown ladders do so only in adaptive. `reasoning.summary` is preserved. Known nonempty targets retain their effort resolution, and target selection/order is unchanged. | | `imageInput?` | `"auto" \| "disabled"` | `"auto"` | `"auto"` publishes image only when every target supports images; `"disabled"` forces text-only (drops image from published modalities and rejects image-bearing requests before dispatch). | | `alias?` | `string` | — | Optional public model id in place of the canonical picker slug. | | `nativeAlias?` | `boolean` | `false` | Let a currently supported bare native id take precedence only for that unqualified id. Bare `gpt-5.6-*` ids use Codex Pool/Direct credentials. Account-qualified routes remain distinct. Provider-qualified routes such as `openai-apikey/gpt-5.6-*` use their configured API-key route and never fall through to the native alias. | diff --git a/docs-site/src/content/docs/ru/guides/combos.md b/docs-site/src/content/docs/ru/guides/combos.md index 53a6b4b5f3..ddd16f6175 100644 --- a/docs-site/src/content/docs/ru/guides/combos.md +++ b/docs-site/src/content/docs/ru/guides/combos.md @@ -177,7 +177,7 @@ Failover намеренно ограничен. Он помогает при п ## Effort по умолчанию -`defaultEffort` заполняет отсутствующий `reasoning.effort`, если задан ненулевой default и список поддерживаемых уровней цели известен и непуст. Поддерживаемое настроенное значение сохраняется; иначе выбирается максимальный поддерживаемый уровень не выше него, а если такого нет — минимальный поддерживаемый уровень. При неизвестном или пустом списке default не добавляется. +`defaultEffort` заполняет отсутствующий `reasoning.effort`, если задан `defaultEffort`, отличный от `null`, и список поддерживаемых уровней цели известен и непуст. Поддерживаемое настроенное значение сохраняется; иначе выбирается максимальный поддерживаемый уровень не выше него, а если такого нет — минимальный поддерживаемый уровень. При неизвестном или пустом списке default не добавляется. Подстановка сохраняет существующий effort и остальные поля reasoning. Нормализация возможностей ниже может отдельно удалить неподдерживаемые параметры effort/thinking. Значения default: `low`, `medium`, `high`, `xhigh`, `max`, `ultra`; отсутствие поля или `null` отключает подстановку. diff --git a/docs-site/src/content/docs/ru/reference/configuration/routing.md b/docs-site/src/content/docs/ru/reference/configuration/routing.md index 029df5594e..595916aebd 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ru/reference/configuration/routing.md @@ -87,7 +87,8 @@ selector-qualified строки и возвращает обычные GPT-ст | `targets` | `{ provider: string; model: string; weight?: number }[]` | required | Упорядоченные конкретные маршруты. `weight` находится в диапазоне 1–10000 и по умолчанию равен `1`. | | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | Стратегия выбора. Порядок целей задаёт приоритет `failover`; значения `weight` определяют взвешивание выборов `round-robin` и `random`; `least-used` следует числу зарегистрированных успешных запросов; `reset-window` следует ближайшему сбросу квоты. | | `stickyLimit?` | `number` | `1` | Число успешных запросов, удерживаемых в одной партии round-robin. Диапазон 1–100. | -| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort` заполняет отсутствующий `reasoning.effort`, если задан ненулевой default и список поддерживаемых уровней цели известен и непуст. Поддерживаемое настроенное значение сохраняется; иначе выбирается максимальный поддерживаемый уровень не выше него, а если такого нет — минимальный поддерживаемый уровень. При неизвестном или пустом списке default не добавляется. | +| `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | `defaultEffort` заполняет отсутствующий `reasoning.effort`, если задан `defaultEffort`, отличный от `null`, и список поддерживаемых уровней цели известен и непуст. Поддерживаемое настроенное значение сохраняется; иначе выбирается максимальный поддерживаемый уровень не выше него, а если такого нет — минимальный поддерживаемый уровень. При неизвестном или пустом списке default не добавляется. | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` вычисляет пересечение известных списков уровней, включая пустые; `"adaptive"` исключает пустые списки. Неизвестные списки не ограничивают пересечение в обоих режимах. При отправке явно пустой список удаляет параметры effort/thinking в обоих режимах, а неизвестный — только в adaptive. `reasoning.summary` сохраняется. Разрешение effort для известных непустых списков, выбор и порядок целей не меняются. | | `alias?` | `string` | — | Необязательный публичный id модели вместо канонического slug в селекторе. | | `nativeAlias?` | `boolean` | `false` | Даёт поддерживаемому bare native id приоритет только для этого неквалифицированного id. Bare `gpt-5.6-*` использует учётные данные Codex Pool/Direct. Маршруты с квалификатором аккаунта остаются отдельными. Провайдер-квалифицированные маршруты, например `openai-apikey/gpt-5.6-*`, используют настроенный API-ключ и никогда не переходят на native alias. | | `displayName?` | `string` | — | Метка только для catalog; для native alias обязательна и не может быть пустой. | diff --git a/docs-site/src/content/docs/tr/reference/configuration/routing.md b/docs-site/src/content/docs/tr/reference/configuration/routing.md index b81f1464cd..b1cbe4b484 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/routing.md +++ b/docs-site/src/content/docs/tr/reference/configuration/routing.md @@ -111,6 +111,7 @@ aileleri kullanamaz. | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | Seçim stratejisi. Hedef sırası `failover` önceliğini belirler; `weight` değerleri `round-robin` ve `random` seçimlerini biçimlendirir; `least-used` kaydedilen başarılı istekleri izler; `reset-window` en yakın kota sıfırlamasını izler. | | `stickyLimit?` | `number` | `1` | Tek bir round-robin grubunda tutulan başarılı istekler. Aralık 1–100. | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | ayarlanmamış | `defaultEffort`, combo varsayılanı null değilse ve hedefin desteklenen seviye listesi bilinen ve boş olmayan bir listeyse eksik `reasoning.effort` değerini doldurur. Yapılandırılmış değer destekleniyorsa korunur; değilse bu değeri aşmayan en yüksek desteklenen seviye, böyle bir seviye yoksa en düşük desteklenen seviye kullanılır. Liste bilinmiyor veya boşsa varsayılan eklenmez. | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"`, boş listeler dahil bilinen hedef seviye listelerinin kesişimini alır; `"adaptive"` boş listeleri çıkarır. Bilinmeyen listeler iki modda da kesişimi sınırlamaz. Gönderimde açıkça boş listeler iki modda effort/thinking denetimlerini kaldırır; bilinmeyen listeler bunu yalnızca adaptive modunda yapar. `reasoning.summary` korunur. Bilinen boş olmayan hedeflerin effort çözümü ve hedef seçimi/sırası değişmez. | | `alias?` | `string` | — | Kurallı seçici slug'ı yerine isteğe bağlı genel model kimliği. | | `nativeAlias?` | `boolean` | `false` | Şu anda desteklenen bir yalın yerel kimliğin yalnızca o niteliksiz kimlik için öncelikli olmasına izin verin. Yalın `gpt-5.6-*` kimlikleri Codex Havuz/Direct kimlik bilgilerini kullanır. Hesap nitelikli rotalar ayrı kalır. `openai-apikey/gpt-5.6-*` gibi sağlayıcı nitelikli rotalar yapılandırılmış API anahtarı rotalarını kullanır ve asla yerel takma ada düşmez. | | `displayName?` | `string` | — | Yalnızca görüntüleme amaçlı katalog etiketi, yerel bir takma ad için gerekli ve boş olmamalıdır. | diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md b/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md index 6232bc0306..fa7d04ffc5 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/routing.md @@ -74,6 +74,7 @@ Codex Auth 页面将此 picker 行为作为选择加入项。关闭它会隐藏 | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | 选择策略。目标顺序表示 `failover` 优先级;`weight` 决定 `round-robin` 和 `random` 的抽取权重;`least-used` 根据记录的成功次数选择;`reset-window` 跟随最近的额度重置。 | | `stickyLimit?` | `number` | `1` | 在单个轮询批次中保留的成功请求数。范围 1–100。 | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | unset | 当 combo 配置了非 null 默认值且目标支持列表已知且非空时,`defaultEffort` 会填充省略的 `reasoning.effort`。目标支持配置值时保留该值,否则选择不高于配置值的最高支持档位;若不存在更低档位,则使用最低支持档位。未知或空列表不会注入默认值。 | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` 对所有已知目标档位列表取交集,包括空列表;`"adaptive"` 排除空列表。未知列表在两种模式下都不限制目录交集。发送时,显式空列表在两种模式下都会移除 effort/thinking 控制;未知列表仅在 adaptive 下移除。`reasoning.summary` 保持不变。已知非空目标的 effort 解析、目标选择和顺序不变。 | | `imageInput?` | `"auto" \| "disabled"` | `"auto"` | `"auto"` 仅在每个目标都支持图片时发布图片能力;`"disabled"` 强制仅文本(从对外能力中去掉图片,并在分发前拒绝带图请求)。 | | `alias?` | `string` | — | 可选的公开 model id,用于替代规范化的选择器 slug。 | | `nativeAlias?` | `boolean` | `false` | 仅让当前受支持的裸原生 id 对该不带限定前缀的 id 优先;带账号或提供方限定的 OpenAI 路由仍是独立路由。 | diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md b/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md index 50ef13c16b..2001cf14a9 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/routing.md @@ -57,6 +57,7 @@ Codex Auth 頁面將此 picker 行為作為選擇加入功能暴露。停用它 | `strategy?` | `"failover" \| "round-robin" \| "random" \| "least-used" \| "reset-window"` | `"failover"` | 選擇策略。目標順序為 `failover` 優先序;`weight` 塑造 `round-robin` 與 `random` 抽選;`least-used` 依循已記錄的成功次數;`reset-window` 依循最早的配額重設。 | | `stickyLimit?` | `number` | `1` | 在一個 round-robin 批次中保留的成功請求數。範圍 1–100。 | | `defaultEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra" \| null` | 未設定 | 當 combo 設定非 null 預設值且目標支援清單已知且非空時,`defaultEffort` 會補入省略的 `reasoning.effort`。目標支援設定值時保留該值,否則選擇不高於設定值的最高支援層級;若沒有更低層級,則使用最低支援層級。未知或空清單不會注入預設值。 | +| `reasoningEffortMode?` | `"strict" \| "adaptive"` | `"strict"` | `"strict"` 對所有已知目標層級清單取交集,包括空清單;`"adaptive"` 排除空清單。未知清單在兩種模式下都不限制目錄交集。傳送時,明確空清單在兩種模式下都會移除 effort/thinking 控制;未知清單只在 adaptive 移除。`reasoning.summary` 保持不變。已知非空目標的 effort 解析、目標選擇及順序不變。 | | `alias?` | `string` | — | 可選的公開模型 id,取代標準 picker slug。 | | `nativeAlias?` | `boolean` | `false` | 讓目前支援的裸原生 id 僅對該未限定 id 取得優先。裸 `gpt-5.6-*` id 使用 Codex 池/Direct 憑證。帳號限定路由保持獨立。供應商限定路由(如 `openai-apikey/gpt-5.6-*`)使用其設定的 API-key 路由,且永不會落到原生別名。 | | `displayName?` | `string` | — | 僅顯示的目錄標籤,對原生別名為必填且非空。 |