From 9976e7d89ec75b6730dac96b14cd65a42678b13e Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Sat, 7 Nov 2026 17:26:56 +0900 Subject: [PATCH 01/11] fix(mistral): bound image pruning memory --- .../openai-chat/mistral-image-limit.ts | 40 ++++++++++++------- structure/providers-and-adapters.md | 2 +- tests/providers/mistral-image-limit.test.ts | 17 ++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/adapters/openai-chat/mistral-image-limit.ts b/src/adapters/openai-chat/mistral-image-limit.ts index 919e49e9ea9..1f09a2a07fa 100644 --- a/src/adapters/openai-chat/mistral-image-limit.ts +++ b/src/adapters/openai-chat/mistral-image-limit.ts @@ -22,6 +22,13 @@ function isRecord(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); } +function hasUsableImageUrl(part: Record): boolean { + if (part.type !== "image_url") return false; + const imageUrl = part.image_url; + return (typeof imageUrl === "string" && imageUrl.length > 0) + || (isRecord(imageUrl) && typeof imageUrl.url === "string" && imageUrl.url.length > 0); +} + /** * Keep the newest images a first-party Mistral request may carry and replace older ones with a * text note, across every message (promoted tool-result images included). Normalizes the outbound @@ -30,21 +37,24 @@ function isRecord(value: unknown): value is Record { export function limitMistralChatImages(messages: unknown, provider: OcxProviderConfig, modelId: string): unknown { if (!isMistralChatTarget(provider) || !Array.isArray(messages)) return messages; const maxImages = mistralMaxImages(provider, modelId); - const images: Array<{ message: number; part: number }> = []; - messages.forEach((message, index) => { - if (!isRecord(message) || !Array.isArray(message.content)) return; - message.content.forEach((part, partIndex) => { - if (isRecord(part) && part.type === "image_url") images.push({ message: index, part: partIndex }); - }); - }); - const excess = images.length - maxImages; - if (excess <= 0) return messages; const note = { type: "text", text: `[image omitted: Mistral accepts at most ${maxImages} images per request for this model; older images were dropped]` }; - const out = [...messages]; - for (const { message, part } of images.slice(0, excess)) { - const original = messages[message] as Record; - if (out[message] === original) out[message] = { ...original, content: [...(original.content as unknown[])] }; - ((out[message] as Record).content as unknown[])[part] = note; + let imagesKept = 0; + let out: unknown[] | undefined; + for (let index = messages.length - 1; index >= 0; index--) { + const message = messages[index]; + if (!isRecord(message) || !Array.isArray(message.content)) continue; + const content = message.content; + let nextContent: unknown[] | undefined; + for (let partIndex = content.length - 1; partIndex >= 0; partIndex--) { + const part = content[partIndex]; + if (!isRecord(part) || !hasUsableImageUrl(part)) continue; + if (imagesKept++ < maxImages) continue; + nextContent ??= content.slice(); + nextContent[partIndex] = note; + } + if (!nextContent) continue; + out ??= messages.slice(); + out[index] = { ...message, content: nextContent }; } - return out; + return out ?? messages; } diff --git a/structure/providers-and-adapters.md b/structure/providers-and-adapters.md index f3306c1551e..c8deaed80b5 100644 --- a/structure/providers-and-adapters.md +++ b/structure/providers-and-adapters.md @@ -3,7 +3,7 @@ Reasoning support, thinking on/off control, real reasoning strengths and the thinking default are independent optional boolean axes in `ReasoningCapabilities`, flattened into `ModelCapabilities` and `CatalogModel`. `src/providers/reasoning-capabilities.ts` resolves fixed destination registry facts beneath operator declarations matched by case-insensitive model ID (without suffix or family matching); absent axes stay unknown and explicit false wins. `thinkingToggleModels` declares wire syntax, including adaptive-only protocols, and never implies an independent on/off capability. Legacy effort declarations infer real strength support only when their effective mapped values include genuine strength levels; enabled/disabled/adaptive aliases retain their existing controls without establishing strength support. Inferred strength support does not opt a legacy toggle serializer into sending an additional effort field; that requires an explicit model capability declaration. Legacy declarations still apply unless `supportsReasoningEffort: false`; an empty effort list does not disable the thinking toggle. `noReasoningModels` suppresses routed reasoning controls; native Codex forwarding keeps its snapshot-owned effort clamp. Captured model policy resolves the same axes without rereading the live registry. [Config validation and merge](config.md#explicit-per-model-capability-declarations) preserve each boolean independently. Native Claude family rules in `src/adapters/anthropic-model-contract.ts` outrank generic toggle serialization: adaptive thinking, the Sonnet 5.5 `between_tools` floor, supported explicit disable and budget/output constraints remain authoritative. See [LM Studio Chat instruction defaults](providers/lm-studio-chat.md). Public MiMo V2.6 is a registry instance with reasoning and toggle support, no real effort strengths, and thinking enabled by default. `src/providers/mimo-thinking.ts` supplies those facts; `thinkingToggleModels` separately declares its existing `thinking.type` wire protocol. Client compatibility alone projects the switch to legacy `none/high` choices. Internal metadata contains no synthetic MiMo effort ladder or high default. Public model rows expose optional `supports_reasoning`, `supports_thinking_toggle`, `supports_reasoning_effort` and `default_thinking_enabled` flags; chained provider discovery reads those flags back without inferring false from absence. Config hints rederive cached projections from their original discovered reasoning metadata. Free, token-plan and third-party MiMo routes keep their existing contracts. -Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it keeps the newest `image_url` parts within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8) and replaces older ones with a text note naming the cap. +Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant auxiliary storage, ignores `image_url` parts without a usable string URL, keeps the newest valid images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. GCP JSON storage in `src/providers/api-keys.ts` and durable-row snapshot/post-commit cleanup in `src/providers/gcp-credential-cleanup.ts` follow [Vertex credential markers](providers/google.md#vertex-credential-markers); malformed blank marker accounts do not block cleanup of valid accounts. Combo target `metered` metadata and configured-key opt-in follow the [runtime consent contract](runtime.md#request-local-target-compatibility). Subscription-to-Cursor-to-key regression coverage in `tests/adapters/anthropic/anthropic-subscription-chain.test.ts` checks unchanged healthy subscription wire bodies, effort, key authentication and cache controls, and structured runTurn 429 propagation. OpenCode Go's exact Gemini pin and credential destination guard follow the [Go Gemini transport contract](providers/google.md#opencode-go-gemini-transport). Configured spend ceilings bind adapter inference executors to the [seed and physical-start contract](transports/responses-spend.md#historical-pool-continuity-and-rollback). Zed completion and auth-recovery replays claim separately; token acquisition stays outside inference accounting. Claude CLI, CodeBuddy and Qoder each count one invocation toward the frozen request limit, seed before spawn and retain a producer through completion. Child-internal retries/tool turns are not separately counted; reported actual usage settles in full, including amounts above the seed estimate. Devin combines only [consecutive same-ID tool results](adapters/registry.md#devin-consecutive-tool-results), using linear accumulation while preserving intervening message slots, image parts and error markers without mutating the parsed request. diff --git a/tests/providers/mistral-image-limit.test.ts b/tests/providers/mistral-image-limit.test.ts index 33b5cf7ee8b..571b34ed461 100644 --- a/tests/providers/mistral-image-limit.test.ts +++ b/tests/providers/mistral-image-limit.test.ts @@ -37,6 +37,23 @@ describe("Mistral image count limit", () => { expect(limitMistralChatImages("not messages", mistral(), "mistral-medium-latest")).toBe("not messages"); const malformed = [null, "text", { role: "user", content: "plain" }, { role: "user", content: [null, { type: "image_url" }] }]; expect(limitMistralChatImages(malformed, mistral(), "mistral-medium-latest")).toBe(malformed); + + const manyMalformed = [{ role: "user", content: Array.from({ length: 10_000 }, () => ({ type: "image_url" })) }]; + expect(limitMistralChatImages(manyMalformed, mistral(), "mistral-medium-latest")).toBe(manyMalformed); + }); + + test("counts only usable image URLs and accepts the OpenAI string form", () => { + const content = [ + ...Array.from({ length: 20 }, () => ({ type: "image_url" })), + ...Array.from({ length: 9 }, (_, n) => ({ type: "image_url", image_url: `https://example.test/${n}.png` })), + ]; + const history = [{ role: "user", content }]; + const out = limitMistralChatImages(history, mistral(), "mistral-medium-latest") as typeof history; + expect(out[0].content.slice(0, 20)).toEqual(content.slice(0, 20)); + expect(out[0].content[20]).toEqual({ + type: "text", text: "[image omitted: Mistral accepts at most 8 images per request for this model; older images were dropped]", + }); + expect(out[0].content.slice(21)).toEqual(content.slice(21)); }); test("keeps the newest images across turns, names the cap and leaves the history untouched", () => { From 0e775d461d2585a2854b0be7326e413de24fd46c Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Sat, 7 Nov 2026 17:42:45 +0900 Subject: [PATCH 02/11] test: strengthen bounded Mistral image pruning regression coverage --- src/adapters/openai-chat/mistral-image-limit.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/adapters/openai-chat/mistral-image-limit.ts b/src/adapters/openai-chat/mistral-image-limit.ts index 1f09a2a07fa..821447e2d07 100644 --- a/src/adapters/openai-chat/mistral-image-limit.ts +++ b/src/adapters/openai-chat/mistral-image-limit.ts @@ -22,6 +22,7 @@ function isRecord(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); } +/** Checks for a nonempty image URL string in either supported shape, not URL syntax or reachability. */ function hasUsableImageUrl(part: Record): boolean { if (part.type !== "image_url") return false; const imageUrl = part.image_url; From 293d2f8d2c79d3ca28d6d7948804d4f85c96f853 Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Sat, 7 Nov 2026 17:42:50 +0900 Subject: [PATCH 03/11] test: strengthen bounded Mistral image pruning regression coverage --- structure/providers-and-adapters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure/providers-and-adapters.md b/structure/providers-and-adapters.md index c8deaed80b5..ea98f6dbff0 100644 --- a/structure/providers-and-adapters.md +++ b/structure/providers-and-adapters.md @@ -3,7 +3,7 @@ Reasoning support, thinking on/off control, real reasoning strengths and the thinking default are independent optional boolean axes in `ReasoningCapabilities`, flattened into `ModelCapabilities` and `CatalogModel`. `src/providers/reasoning-capabilities.ts` resolves fixed destination registry facts beneath operator declarations matched by case-insensitive model ID (without suffix or family matching); absent axes stay unknown and explicit false wins. `thinkingToggleModels` declares wire syntax, including adaptive-only protocols, and never implies an independent on/off capability. Legacy effort declarations infer real strength support only when their effective mapped values include genuine strength levels; enabled/disabled/adaptive aliases retain their existing controls without establishing strength support. Inferred strength support does not opt a legacy toggle serializer into sending an additional effort field; that requires an explicit model capability declaration. Legacy declarations still apply unless `supportsReasoningEffort: false`; an empty effort list does not disable the thinking toggle. `noReasoningModels` suppresses routed reasoning controls; native Codex forwarding keeps its snapshot-owned effort clamp. Captured model policy resolves the same axes without rereading the live registry. [Config validation and merge](config.md#explicit-per-model-capability-declarations) preserve each boolean independently. Native Claude family rules in `src/adapters/anthropic-model-contract.ts` outrank generic toggle serialization: adaptive thinking, the Sonnet 5.5 `between_tools` floor, supported explicit disable and budget/output constraints remain authoritative. See [LM Studio Chat instruction defaults](providers/lm-studio-chat.md). Public MiMo V2.6 is a registry instance with reasoning and toggle support, no real effort strengths, and thinking enabled by default. `src/providers/mimo-thinking.ts` supplies those facts; `thinkingToggleModels` separately declares its existing `thinking.type` wire protocol. Client compatibility alone projects the switch to legacy `none/high` choices. Internal metadata contains no synthetic MiMo effort ladder or high default. Public model rows expose optional `supports_reasoning`, `supports_thinking_toggle`, `supports_reasoning_effort` and `default_thinking_enabled` flags; chained provider discovery reads those flags back without inferring false from absence. Config hints rederive cached projections from their original discovered reasoning metadata. Free, token-plan and third-party MiMo routes keep their existing contracts. -Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant auxiliary storage, ignores `image_url` parts without a usable string URL, keeps the newest valid images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. +Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant traversal bookkeeping (excluding the returned copy-on-write message/content arrays, whose size still scales with the input), ignores `image_url` parts without a usable string URL, keeps the newest valid images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. GCP JSON storage in `src/providers/api-keys.ts` and durable-row snapshot/post-commit cleanup in `src/providers/gcp-credential-cleanup.ts` follow [Vertex credential markers](providers/google.md#vertex-credential-markers); malformed blank marker accounts do not block cleanup of valid accounts. Combo target `metered` metadata and configured-key opt-in follow the [runtime consent contract](runtime.md#request-local-target-compatibility). Subscription-to-Cursor-to-key regression coverage in `tests/adapters/anthropic/anthropic-subscription-chain.test.ts` checks unchanged healthy subscription wire bodies, effort, key authentication and cache controls, and structured runTurn 429 propagation. OpenCode Go's exact Gemini pin and credential destination guard follow the [Go Gemini transport contract](providers/google.md#opencode-go-gemini-transport). Configured spend ceilings bind adapter inference executors to the [seed and physical-start contract](transports/responses-spend.md#historical-pool-continuity-and-rollback). Zed completion and auth-recovery replays claim separately; token acquisition stays outside inference accounting. Claude CLI, CodeBuddy and Qoder each count one invocation toward the frozen request limit, seed before spawn and retain a producer through completion. Child-internal retries/tool turns are not separately counted; reported actual usage settles in full, including amounts above the seed estimate. Devin combines only [consecutive same-ID tool results](adapters/registry.md#devin-consecutive-tool-results), using linear accumulation while preserving intervening message slots, image parts and error markers without mutating the parsed request. From ba424dd2da98bad2824c099489736bfba3cc7538 Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Sat, 7 Nov 2026 17:42:55 +0900 Subject: [PATCH 04/11] test: strengthen bounded Mistral image pruning regression coverage --- tests/providers/mistral-image-limit.test.ts | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/providers/mistral-image-limit.test.ts b/tests/providers/mistral-image-limit.test.ts index 571b34ed461..da733371f88 100644 --- a/tests/providers/mistral-image-limit.test.ts +++ b/tests/providers/mistral-image-limit.test.ts @@ -56,6 +56,48 @@ describe("Mistral image count limit", () => { expect(out[0].content.slice(21)).toEqual(content.slice(21)); }); + test("prunes a large valid history without mutating input or copying untouched turns", () => { + const first = Array.from({ length: 5_000 }, (_, n) => image(n)); + const boundary = Array.from({ length: 4_996 }, (_, n) => image(5_000 + n)); + const newest = Array.from({ length: 4 }, (_, n) => image(9_996 + n)); + const history = [ + { role: "system", content: "keep this context" }, + { role: "user", content: first }, + { role: "assistant", content: "preserve turn order" }, + { role: "user", content: boundary }, + { role: "user", content: newest }, + ]; + for (const content of [first, boundary, newest]) { + for (const part of content) { Object.freeze(part.image_url); Object.freeze(part); } + Object.freeze(content); + } + for (const message of history) Object.freeze(message); + Object.freeze(history); + const before = JSON.stringify(history); + + const out = limitMistralChatImages(history, mistral(), "mistral-medium-latest") as typeof history; + expect(kept(out)).toEqual([9_992, 9_993, 9_994, 9_995, 9_996, 9_997, 9_998, 9_999]); + expect(out).not.toBe(history); + expect(out).toHaveLength(history.length); + expect(out[0]).toBe(history[0]); + expect(out[2]).toBe(history[2]); + expect(out[4]).toBe(history[4]); + expect(out[1]).not.toBe(history[1]); + expect(out[3]).not.toBe(history[3]); + const firstOut = out[1].content as unknown[]; + const boundaryOut = out[3].content as unknown[]; + const note = { type: "text", text: "[image omitted: Mistral accepts at most 8 images per request for this model; older images were dropped]" }; + expect(firstOut).toHaveLength(first.length); + expect(boundaryOut).toHaveLength(boundary.length); + expect(firstOut.every(part => part === firstOut[0])).toBe(true); + expect(firstOut[0]).toEqual(note); + expect(boundaryOut.slice(0, -4).every(part => part === firstOut[0])).toBe(true); + expect(boundaryOut.slice(-4)).toEqual(boundary.slice(-4)); + expect(boundaryOut.at(-1)).toBe(boundary.at(-1)); + expect(JSON.stringify(history)).toBe(before); + expect(kept(history)).toHaveLength(10_000); + }); + test("keeps the newest images across turns, names the cap and leaves the history untouched", () => { const history = messages(21); const before = structuredClone(history); From 1ba17d52b3f614b5e89bb14658e3b1738fb594cd Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Sun, 8 Nov 2026 19:19:24 +0900 Subject: [PATCH 05/11] test(mistral): guard pruning bookkeeping and clarify URL counting --- structure/providers-and-adapters.md | 2 +- tests/providers/mistral-image-limit.test.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/structure/providers-and-adapters.md b/structure/providers-and-adapters.md index 7a81da566e7..e01d46619cd 100644 --- a/structure/providers-and-adapters.md +++ b/structure/providers-and-adapters.md @@ -3,7 +3,7 @@ Reasoning support, thinking on/off control, real reasoning strengths and the thinking default are independent optional boolean axes in `ReasoningCapabilities`, flattened into `ModelCapabilities` and `CatalogModel`. `src/providers/reasoning-capabilities.ts` resolves fixed destination registry facts beneath operator declarations matched by case-insensitive model ID (without suffix or family matching); absent axes stay unknown and explicit false wins. `thinkingToggleModels` declares wire syntax, including adaptive-only protocols, and never implies an independent on/off capability. Legacy effort declarations infer real strength support only when their effective mapped values include genuine strength levels; enabled/disabled/adaptive aliases retain their existing controls without establishing strength support. Inferred strength support does not opt a legacy toggle serializer into sending an additional effort field; that requires an explicit model capability declaration. Legacy declarations still apply unless `supportsReasoningEffort: false`; an empty effort list does not disable the thinking toggle. `noReasoningModels` suppresses routed reasoning controls; native Codex forwarding keeps its snapshot-owned effort clamp. Captured model policy resolves the same axes without rereading the live registry. [Config validation and merge](config.md#explicit-per-model-capability-declarations) preserve each boolean independently. Native Claude family rules in `src/adapters/anthropic-model-contract.ts` outrank generic toggle serialization: adaptive thinking, the Sonnet 5.5 `between_tools` floor, supported explicit disable and budget/output constraints remain authoritative. See [LM Studio Chat instruction defaults](providers/lm-studio-chat.md). Public MiMo V2.6 is a registry instance with reasoning and toggle support, no real effort strengths, and thinking enabled by default. `src/providers/mimo-thinking.ts` supplies those facts; `thinkingToggleModels` separately declares its existing `thinking.type` wire protocol. Client compatibility alone projects the switch to legacy `none/high` choices. Internal metadata contains no synthetic MiMo effort ladder or high default. Public model rows expose optional `supports_reasoning`, `supports_thinking_toggle`, `supports_reasoning_effort` and `default_thinking_enabled` flags; chained provider discovery reads those flags back without inferring false from absence. Config hints rederive cached projections from their original discovered reasoning metadata. Free, token-plan and third-party MiMo routes keep their existing contracts. -Canonical Mistral request fields, developer defaults, effort clamping and native typed-content projection follow the [Chat wire policy](providers/mistral-chat.md). Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant traversal bookkeeping (excluding the returned copy-on-write message/content arrays, whose size still scales with the input), ignores `image_url` parts without a usable string URL, keeps the newest valid images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. +Canonical Mistral request fields, developer defaults, effort clamping and native typed-content projection follow the [Chat wire policy](providers/mistral-chat.md). Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant traversal bookkeeping (excluding the returned copy-on-write message/content arrays, whose size still scales with the input), counts an `image_url` part only when `image_url` or `image_url.url` is a nonempty string (without validating URL syntax or reachability), keeps the newest counted images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. GCP JSON storage in `src/providers/api-keys.ts` and durable-row snapshot/post-commit cleanup in `src/providers/gcp-credential-cleanup.ts` follow [Vertex credential markers](providers/google.md#vertex-credential-markers); malformed blank marker accounts do not block cleanup of valid accounts. Combo target `metered` metadata and configured-key opt-in follow the [runtime consent contract](runtime.md#request-local-target-compatibility). Subscription-to-Cursor-to-key regression coverage in `tests/adapters/anthropic/anthropic-subscription-chain.test.ts` checks unchanged healthy subscription wire bodies, effort, key authentication and cache controls, and structured runTurn 429 propagation. OpenCode Go's exact Gemini pin and credential destination guard follow the [Go Gemini transport contract](providers/google.md#opencode-go-gemini-transport). Configured spend ceilings bind adapter inference executors to the [seed and physical-start contract](transports/responses-spend.md#historical-pool-continuity-and-rollback). Zed completion and auth-recovery replays claim separately; token acquisition stays outside inference accounting. Claude CLI, CodeBuddy and Qoder each count one invocation toward the frozen request limit, seed before spawn and retain a producer through completion. Child-internal retries/tool turns are not separately counted; reported actual usage settles in full, including amounts above the seed estimate. Devin combines only [consecutive same-ID tool results](adapters/registry.md#devin-consecutive-tool-results), using linear accumulation while preserving intervening message slots, image parts and error markers without mutating the parsed request. diff --git a/tests/providers/mistral-image-limit.test.ts b/tests/providers/mistral-image-limit.test.ts index da733371f88..538fddbf3b2 100644 --- a/tests/providers/mistral-image-limit.test.ts +++ b/tests/providers/mistral-image-limit.test.ts @@ -1,4 +1,5 @@ import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; import { limitMistralChatImages, mistralMaxImages, MISTRAL_DEFAULT_MAX_IMAGES } from "../../src/adapters/openai-chat/mistral-image-limit"; import { buildOpenAIChatPassthroughRequest } from "../../src/adapters/openai-chat/passthrough"; import { handleResponses } from "../../src/server/responses/core"; @@ -7,6 +8,7 @@ import { getProviderRegistryEntry } from "../../src/providers/registry"; import { resetProviderRequestPacingForTest } from "../../src/providers/request-pacing"; import type { OcxConfig, OcxProviderConfig } from "../../src/types"; import { acquireOwnedSpendHome } from "../helpers/owned-spend-home"; +import { repoPath } from "../helpers/repo-root"; const mistral = (overrides: Partial = {}): OcxProviderConfig => ({ ...providerConfigSeed(getProviderRegistryEntry("mistral")!), apiKey: "test-mistral-images", ...overrides }); @@ -56,6 +58,17 @@ describe("Mistral image count limit", () => { expect(out[0].content.slice(21)).toEqual(content.slice(21)); }); + test("does not reintroduce the historical per-image coordinate collection", () => { + const source = readFileSync(repoPath("src/adapters/openai-chat/mistral-image-limit.ts"), "utf8"); + const start = source.indexOf("export function limitMistralChatImages("); + expect(start).toBeGreaterThanOrEqual(0); + // Narrow source oracle for the old images.push({ message, part }) index, not + // a heap bound: required copy-on-write message/content arrays remain allowed. + const coordinatePush = /\.push\s*\(\s*\{\s*(?:message\s*:\s*\w+\s*,\s*part\s*:\s*\w+|part\s*:\s*\w+\s*,\s*message\s*:\s*\w+)\s*\}\s*\)/; + expect("images.push({ message: index, part: partIndex })").toMatch(coordinatePush); + expect(source.slice(start)).not.toMatch(coordinatePush); + }); + test("prunes a large valid history without mutating input or copying untouched turns", () => { const first = Array.from({ length: 5_000 }, (_, n) => image(n)); const boundary = Array.from({ length: 4_996 }, (_, n) => image(5_000 + n)); From 97dee080c36ed4e268b8cc9126fda78c0ff34af7 Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Mon, 9 Nov 2026 10:09:15 +0900 Subject: [PATCH 06/11] fix(mistral): replace malformed image references before dispatch --- .../openai-chat/mistral-image-limit.ts | 8 ++++--- structure/providers-and-adapters.md | 2 +- tests/providers/mistral-image-limit.test.ts | 22 ++++++++++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/adapters/openai-chat/mistral-image-limit.ts b/src/adapters/openai-chat/mistral-image-limit.ts index 821447e2d07..b7837b782e2 100644 --- a/src/adapters/openai-chat/mistral-image-limit.ts +++ b/src/adapters/openai-chat/mistral-image-limit.ts @@ -39,6 +39,7 @@ export function limitMistralChatImages(messages: unknown, provider: OcxProviderC if (!isMistralChatTarget(provider) || !Array.isArray(messages)) return messages; const maxImages = mistralMaxImages(provider, modelId); const note = { type: "text", text: `[image omitted: Mistral accepts at most ${maxImages} images per request for this model; older images were dropped]` }; + const missingNote = { type: "text", text: "[image omitted: missing or malformed image URL]" }; let imagesKept = 0; let out: unknown[] | undefined; for (let index = messages.length - 1; index >= 0; index--) { @@ -48,10 +49,11 @@ export function limitMistralChatImages(messages: unknown, provider: OcxProviderC let nextContent: unknown[] | undefined; for (let partIndex = content.length - 1; partIndex >= 0; partIndex--) { const part = content[partIndex]; - if (!isRecord(part) || !hasUsableImageUrl(part)) continue; - if (imagesKept++ < maxImages) continue; + if (!isRecord(part) || part.type !== "image_url") continue; + const usable = hasUsableImageUrl(part); + if (usable && imagesKept++ < maxImages) continue; nextContent ??= content.slice(); - nextContent[partIndex] = note; + nextContent[partIndex] = usable ? note : missingNote; } if (!nextContent) continue; out ??= messages.slice(); diff --git a/structure/providers-and-adapters.md b/structure/providers-and-adapters.md index 5617f8bd6a9..66e2a74b6a3 100644 --- a/structure/providers-and-adapters.md +++ b/structure/providers-and-adapters.md @@ -3,7 +3,7 @@ Reasoning support, thinking on/off control, real reasoning strengths and the thinking default are independent optional boolean axes in `ReasoningCapabilities`, flattened into `ModelCapabilities` and `CatalogModel`. `src/providers/reasoning-capabilities.ts` resolves fixed destination registry facts beneath operator declarations matched by case-insensitive model ID (without suffix or family matching); absent axes stay unknown and explicit false wins. `thinkingToggleModels` declares wire syntax, including adaptive-only protocols, and never implies an independent on/off capability. Legacy effort declarations infer real strength support only when their effective mapped values include genuine strength levels; enabled/disabled/adaptive aliases retain their existing controls without establishing strength support. Inferred strength support does not opt a legacy toggle serializer into sending an additional effort field; that requires an explicit model capability declaration. Legacy declarations still apply unless `supportsReasoningEffort: false`; an empty effort list does not disable the thinking toggle. `noReasoningModels` suppresses routed reasoning controls; native Codex forwarding keeps its snapshot-owned effort clamp. Captured model policy resolves the same axes without rereading the live registry. [Config validation and merge](config.md#explicit-per-model-capability-declarations) preserve each boolean independently. Native Claude family rules in `src/adapters/anthropic-model-contract.ts` outrank generic toggle serialization: adaptive thinking, the Sonnet 5.5 `between_tools` floor, supported explicit disable and budget/output constraints remain authoritative. See [LM Studio Chat instruction defaults](providers/lm-studio-chat.md). Public MiMo V2.6 is a registry instance with reasoning and toggle support, no real effort strengths, and thinking enabled by default. `src/providers/mimo-thinking.ts` supplies those facts; `thinkingToggleModels` separately declares its existing `thinking.type` wire protocol. Client compatibility alone projects the switch to legacy `none/high` choices. Internal metadata contains no synthetic MiMo effort ladder or high default. Public model rows expose optional `supports_reasoning`, `supports_thinking_toggle`, `supports_reasoning_effort` and `default_thinking_enabled` flags; chained provider discovery reads those flags back without inferring false from absence. Config hints rederive cached projections from their original discovered reasoning metadata. Free, token-plan and third-party MiMo routes keep their existing contracts. -Canonical Mistral request fields, developer defaults, effort clamping and native typed-content projection follow the [Chat wire policy](providers/mistral-chat.md). Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant traversal bookkeeping (excluding the returned copy-on-write message/content arrays, whose size still scales with the input), counts an `image_url` part only when `image_url` or `image_url.url` is a nonempty string (without validating URL syntax or reachability), keeps the newest counted images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. +Canonical Mistral request fields, developer defaults, effort clamping and native typed-content projection follow the [Chat wire policy](providers/mistral-chat.md). Canonical key-auth Mistral Chat at `https://api.mistral.ai/v1` defaults to a patient bounded same-key 429 policy (six replays, ten-second fallback) in `src/providers/key-failover.ts`; explicit retry settings/opt-out win. The Mistral preset forwards supplied prompt-cache keys unless explicitly disabled. `src/providers/key-failover.ts` and transport selection use the same canonical-destination check, including custom provider names. `src/providers/mistral-transport.ts` shares explicit Retry-After deadlines across calls by actual credential digest and exact model, with bounded memory/waiters and cancellation. Without Retry-After, only a real 429 with validated exhausted minute capacity or a measured query charge above remaining but within the full token allowance activates a conservative 60-second recovery hold. This local policy does not claim the server reset instant. One probe gates concurrent recovery callers; a successful probe clears recovery unless a newer refusal intervened. Healthy traffic never predicts future query/cache cost; unknown, zero-entitlement, exhausted-monthly and above-full-allowance evidence does not invent a minute reset. Oversized Retry-After stops same-key replay rather than shortening the server delay: a same-target retry hold in `src/lib/upstream-retry.ts` is checked only by the same-key 429 loops, so key rotation and the original upstream status, body and Retry-After stay available. Either `requestPacing.enabled: false` or `retryOn429.respectRetryAfter: false` disables the shared hold. `tests/providers/mistral-recovery.test.ts` covers GLM/Large 4, destination/key/model isolation, absent headers, cache affinity, streamed/buffered replay, cancellation and send ceilings. There is no mid-stream replay or automatic compaction change. The same destination check gates `src/adapters/openai-chat/mistral-image-limit.ts`, the last outbound-copy step of `messagesToChatFormat` and of the native Chat passthrough (`src/adapters/openai-chat/passthrough.ts`): it walks content in reverse with constant traversal bookkeeping (excluding the returned copy-on-write message/content arrays, whose size still scales with the input), counts an `image_url` part only when `image_url` or `image_url.url` is a nonempty string (without validating URL syntax or reachability), replaces image parts lacking either nonempty string shape with a text note without consuming the cap, keeps the newest counted images within `modelCapabilities..mistralMaxImages` (operator declaration over the destination registry row, else the documented 8), and copy-on-write replaces older ones with a text note naming the cap. GCP JSON storage in `src/providers/api-keys.ts` and durable-row snapshot/post-commit cleanup in `src/providers/gcp-credential-cleanup.ts` follow [Vertex credential markers](providers/google.md#vertex-credential-markers); malformed blank marker accounts do not block cleanup of valid accounts. Vertex ADC dispatch authority is described in [Vertex API-key endpoint selection](providers/google.md#vertex-api-key-endpoint-selection). Combo target `metered` metadata and configured-key opt-in follow the [runtime consent contract](runtime.md#request-local-target-compatibility). Subscription-to-Cursor-to-key regression coverage in `tests/adapters/anthropic/anthropic-subscription-chain.test.ts` checks unchanged healthy subscription wire bodies, effort, key authentication and cache controls, and structured runTurn 429 propagation. OpenCode Go's exact Gemini pin and credential destination guard follow the [Go Gemini transport contract](providers/google.md#opencode-go-gemini-transport). Configured spend ceilings bind adapter inference executors to the [seed and physical-start contract](transports/responses-spend.md#historical-pool-continuity-and-rollback). Zed completion and auth-recovery replays claim separately; token acquisition stays outside inference accounting. Claude CLI, CodeBuddy and Qoder each count one invocation toward the frozen request limit, seed before spawn and retain a producer through completion. Child-internal retries/tool turns are not separately counted; reported actual usage settles in full, including amounts above the seed estimate. Devin combines only [consecutive same-ID tool results](adapters/registry.md#devin-consecutive-tool-results), using linear accumulation while preserving intervening message slots, image parts and error markers without mutating the parsed request. diff --git a/tests/providers/mistral-image-limit.test.ts b/tests/providers/mistral-image-limit.test.ts index 538fddbf3b2..fd307bcb0b6 100644 --- a/tests/providers/mistral-image-limit.test.ts +++ b/tests/providers/mistral-image-limit.test.ts @@ -38,10 +38,12 @@ describe("Mistral image count limit", () => { expect(limitMistralChatImages(many, mistral({ baseUrl: "https://gateway.example/v1" }), "mistral-medium-latest")).toBe(many); expect(limitMistralChatImages("not messages", mistral(), "mistral-medium-latest")).toBe("not messages"); const malformed = [null, "text", { role: "user", content: "plain" }, { role: "user", content: [null, { type: "image_url" }] }]; - expect(limitMistralChatImages(malformed, mistral(), "mistral-medium-latest")).toBe(malformed); + expect((limitMistralChatImages(malformed, mistral(), "mistral-medium-latest") as typeof malformed).slice(0, 3)).toEqual(malformed.slice(0, 3)); const manyMalformed = [{ role: "user", content: Array.from({ length: 10_000 }, () => ({ type: "image_url" })) }]; - expect(limitMistralChatImages(manyMalformed, mistral(), "mistral-medium-latest")).toBe(manyMalformed); + const sanitized = limitMistralChatImages(manyMalformed, mistral(), "mistral-medium-latest") as typeof manyMalformed; + expect(sanitized[0].content.every(part => part.type === "text")).toBe(true); + expect(manyMalformed[0].content.every(part => part.type === "image_url")).toBe(true); }); test("counts only usable image URLs and accepts the OpenAI string form", () => { @@ -51,7 +53,7 @@ describe("Mistral image count limit", () => { ]; const history = [{ role: "user", content }]; const out = limitMistralChatImages(history, mistral(), "mistral-medium-latest") as typeof history; - expect(out[0].content.slice(0, 20)).toEqual(content.slice(0, 20)); + expect(out[0].content.slice(0, 20).every(part => part.type === "text")).toBe(true); expect(out[0].content[20]).toEqual({ type: "text", text: "[image omitted: Mistral accepts at most 8 images per request for this model; older images were dropped]", }); @@ -139,6 +141,20 @@ describe("Mistral image count limit", () => { expect(mistralMaxImages(mistral(), "constructor")).toBe(MISTRAL_DEFAULT_MAX_IMAGES); }); + test("native Chat replaces missing image references without consuming the valid cap", () => { + const bad = [{ type: "image_url" }, { type: "image_url", image_url: "" }, { type: "image_url", image_url: { url: 42 } }]; + const content = [...bad, ...Array.from({ length: 8 }, (_, n) => image(n)), ...bad]; + const history = [{ role: "user", content }]; + const before = JSON.stringify(history); + const request = buildOpenAIChatPassthroughRequest(mistral(), { messages: history }, "mistral-medium-latest", false); + const sent = JSON.parse(request.body).messages; + expect(sent[0].content.filter((part: { type: string }) => part.type === "image_url")).toHaveLength(8); + expect(kept(sent)).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(JSON.stringify(history)).toBe(before); + const other = limitMistralChatImages(history, mistral({ baseUrl: "https://gateway.example/v1" }), "mistral-medium-latest"); + expect(other).toBe(history); + }); + test("native Chat Completions bodies are capped the same way", () => { const history = messages(21); const request = buildOpenAIChatPassthroughRequest(mistral(), { messages: history }, "deepseek-v4-1-flash-1017", false); From 6b717cf455a364a36fefe01beff0412c2cea546a Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Mon, 9 Nov 2026 13:34:24 +0900 Subject: [PATCH 07/11] docs(mistral): explain malformed image handling across locales --- .../content/docs/fr/reference/configuration/providers.md | 4 ++++ .../content/docs/ja/reference/configuration/providers.md | 4 ++++ .../content/docs/ko/reference/configuration/providers.md | 4 ++++ .../src/content/docs/reference/configuration/providers.md | 6 ++++++ .../content/docs/ru/reference/configuration/providers.md | 4 ++++ .../content/docs/tr/reference/configuration/providers.md | 4 ++++ .../content/docs/zh-cn/reference/configuration/providers.md | 4 ++++ .../content/docs/zh-tw/reference/configuration/providers.md | 4 ++++ 8 files changed, 34 insertions(+) diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index c4bd73f020c..0d51eb3250e 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -6,6 +6,10 @@ description: Entrées du fournisseur, authentification, points de terminaison, c Un fournisseur indique à opencodex où se trouve un modèle, quel adaptateur de protocole il utilise et comment les requêtes sont authentifiées. +## Nombre d’images par requête Mistral + +La limite d’images de Mistral et le réglage `mistralMaxImages` sont décrits dans la [référence canonique en anglais](/reference/configuration/providers/#mistral-images-per-request). Les références d’image absentes, vides ou non textuelles sont remplacées par du texte dans la copie sortante et ne consomment pas cette limite. Seules les chaînes non vides sont comptées ; la syntaxe et l’accessibilité des URL ne sont pas vérifiées. Les deux formes acceptées sont `image_url` et `image_url.url`. Les autres destinations restent inchangées. + ## Contexte des fournisseurs personnalisés En l’absence de fenêtre déclarée ou configurée, OpenCodex utilise les métadonnées Azure, puis celles du modèle dans les tables embarquées du fabricant ou des agrégateurs, puis le Context cap activé, et enfin 128k. Le minimum des valeurs déclarées/configurées est conservé et le cap reste une limite. Les fournisseurs intégrés ne changent pas. L’ID exact est prioritaire ; les variantes de casse ou point/tiret doivent être non ambiguës. L’estimation affecte la compaction de Codex et les catalogues clients, dont Claude, mais ne prouve pas la capacité du relais pour le routage. Configurez modelContextWindows ou Context cap si le relais accepte moins. Aucun cap n’est activé automatiquement et aucune requête de test n’est envoyée au modèle. Les limites de l’API OpenAI ne sont pas celles de l’abonnement Codex. diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index 3459c6b2caf..38d3b55b42e 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -5,6 +5,10 @@ description: プロバイダー エントリ、認証、エンドポイント、 プロバイダーは、opencodex に、モデルが存在する場所、モデルが通信するワイヤー アダプター、およびリクエストの認証方法を伝えます。 +## Mistral のリクエストごとの画像数 + +Mistral の画像数制限と `mistralMaxImages` の設定は[英語の正規リファレンス](/reference/configuration/providers/#mistral-images-per-request)を参照してください。画像参照が欠落、空、または文字列でない場合、送信用コピー内でテキストに置き換えられ、画像数の上限を消費しません。空でない文字列のみを数え、URL の構文や到達可能性は検証しません。 対象の文字列は `image_url` または `image_url.url` に指定します。他の送信先の動作は変わりません。 + ## カスタムプロバイダーのコンテキスト モデルのウィンドウが未報告・未設定の場合、Azure メタデータ、モデル ID による同梱ベンダー/集約メタデータ、有効な Context cap、128k の順で補完します。報告値と設定値は小さい方を使い、Context cap は引き続き上限となります。組み込みプロバイダーは変更しません。完全一致を優先し、大文字小文字やドット/ハイフンの別表記は曖昧でない場合だけ認めます。推定は Codex の圧縮予算や Claude を含むクライアントのカタログに反映されますが、ルーティングの実測能力とは区別します。同名モデルでも中継サービスの容量は保証されません。必要に応じて modelContextWindows または Context cap を設定してください。自動的な cap の有効化やモデルへの試験リクエストは行いません。OpenAI API の値は Codex サブスクリプションの上限ではありません。 diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index d59609c3db8..4aebfa94372 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -5,6 +5,10 @@ description: 공급자 항목, 인증, 엔드포인트, 모델 카탈로그, 할 공급자는 opencodex에 모델의 위치, 사용하는 와이어 어댑터, 요청 인증 방식을 알려줍니다. +## Mistral 요청당 이미지 수 + +Mistral 요청의 이미지 수 제한과 `mistralMaxImages` 설정은 [영문 기준 문서](/reference/configuration/providers/#mistral-images-per-request)를 참조하세요. 누락되거나 비어 있거나 문자열이 아닌 이미지 참조는 전송용 사본에서 텍스트로 대체되며 이미지 한도를 소비하지 않습니다. 비어 있지 않은 문자열만 계산하고 URL 문법이나 접근 가능성은 검증하지 않습니다. 문자열은 `image_url` 또는 `image_url.url`에 지정합니다. 다른 대상의 동작은 바뀌지 않습니다. + ## 사용자 지정 공급자의 컨텍스트 모델 창이 보고되거나 설정되지 않으면 Azure 메타데이터, 모델 ID로 찾은 번들 공급업체/집계 메타데이터, 활성 Context cap, 128k 순서로 보완합니다. 보고된 값과 설정 값 중 작은 값을 유지하고 Context cap은 계속 상한으로 적용됩니다. 기본 공급자는 바뀌지 않습니다. 정확한 ID를 우선하며 대소문자 및 점/하이픈 변형은 충돌이 없을 때만 허용합니다. 추정값은 Codex 압축 예산과 Claude 등 클라이언트 카탈로그에 반영되지만 라우팅의 검증된 용량으로 취급하지 않습니다. 같은 모델 이름이 중계 서비스의 실제 용량을 보장하지 않으므로 modelContextWindows 또는 Context cap으로 실제 상한을 설정하세요. cap을 자동으로 켜거나 모델 시험 요청을 보내지 않습니다. OpenAI API 값은 Codex 구독의 제한이 아닙니다. diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index d9cca538c98..d394583751d 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -53,6 +53,12 @@ any other. The measured caps ship as registry keeps them; any other model uses 8. Declare a model's own cap with `modelCapabilities: { "mistral-small-2603": { mistralMaxImages: 8 } }` (an integer from 1 to 1000). The declaration outranks the registry row, and PATCH `null` on the axis removes it. +Only `image_url` parts with a nonempty string in `image_url` or `image_url.url` count toward +the cap; this check does not validate URL syntax or reachability. Parts with missing, empty, +or non-string image references are replaced with `[image omitted: missing or malformed image URL]` +in the outbound copy without consuming the image cap, even when the valid-image count is below +the cap. + Pruning adds no separate upload or persistence. Images retained within the cap are sent to Mistral in the outbound request. Other destinations are unchanged. diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index 4ffac816f4d..e92407e1d9e 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -6,6 +6,10 @@ description: Записи провайдеров, аутентификация, Провайдер сообщает opencodex, где живёт модель, на каком wire-adapter'е она работает и как аутентифицируются запросы. +## Число изображений в запросе Mistral + +Ограничение числа изображений Mistral и настройка `mistralMaxImages` описаны в [каноническом справочнике на английском языке](/reference/configuration/providers/#mistral-images-per-request). Отсутствующие, пустые или нестроковые ссылки на изображения заменяются текстом в исходящей копии и не расходуют лимит изображений. Учитываются только непустые строки; синтаксис и доступность URL не проверяются. Поддерживаются строки в `image_url` или `image_url.url`. Поведение других назначений не меняется. + ## Контекст пользовательских провайдеров Если окно модели не сообщено и не задано, OpenCodex использует метаданные Azure, затем встроенные данные производителя или агрегатора по ID модели, затем включённый Context cap и, наконец, 128k. Из сообщённого и заданного значений выбирается меньшее; cap остаётся верхней границей. Встроенные провайдеры не меняются. Точный ID имеет приоритет, варианты регистра и точки/дефиса допускаются только без неоднозначности. Оценка влияет на сжатие Codex и каталоги клиентов, включая Claude, но не считается подтверждённой ёмкостью для маршрутизации. Одинаковое имя не гарантирует возможности посредника: задайте modelContextWindows или Context cap для его реального предела. Cap не включается автоматически, запросы к модели для проверки не отправляются. Значения OpenAI API не являются лимитами подписки Codex. diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index 5c9a70f54a1..c212f477866 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -6,6 +6,10 @@ description: Sağlayıcı girdileri, kimlik doğrulama, uç noktalar, model kata Bir sağlayıcı, opencodex'e bir modelin nerede yaşadığını, hangi hat adaptörünü konuştuğunu ve isteklerin nasıl doğrulandığını söyler. +## Mistral isteği başına görüntü sayısı + +Mistral görüntü sınırı ve `mistralMaxImages` ayarı için [İngilizce temel başvuruya](/reference/configuration/providers/#mistral-images-per-request) bakın. Eksik, boş veya dize olmayan görüntü başvuruları, giden kopyada metinle değiştirilir ve görüntü sınırını tüketmez. Yalnızca boş olmayan dizeler sayılır; URL sözdizimi veya erişilebilirliği doğrulanmaz. Dizeler `image_url` veya `image_url.url` içinde olabilir. Diğer hedeflerin davranışı değişmez. + ## Özel sağlayıcılarda bağlam penceresi Model penceresi bildirilmemiş veya yapılandırılmamışsa sırasıyla Azure meta verisi, model kimliğiyle bulunan yerleşik üretici/toplayıcı meta verisi, etkin Context cap ve 128k kullanılır. Bildirilen ve yapılandırılan değerlerin küçüğü korunur; cap üst sınır olmaya devam eder. Yerleşik sağlayıcılar değişmez. Tam kimlik önceliklidir; büyük/küçük harf ve nokta/tire varyantları yalnızca belirsiz değilse kabul edilir. Tahmin, Codex sıkıştırmasını ve Claude dahil istemci kataloglarını etkiler ancak yönlendirme için doğrulanmış kapasite sayılmaz. Aynı model adı aracının gerçek kapasitesini garanti etmez; modelContextWindows veya Context cap ile gerçek sınırı belirtin. Cap otomatik açılmaz ve modele test isteği gönderilmez. OpenAI API sınırları Codex abonelik sınırları değildir. diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index 41890df5aa3..4141519338c 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -5,6 +5,10 @@ description: 提供者条目、身份验证、端点、模型目录、配额、 提供者用于告诉 opencodex 模型位于哪里、使用哪种线协议适配器,以及请求如何进行身份验证。 +## Mistral 每次请求的图片数量 + +Mistral 图片数量限制及 `mistralMaxImages` 设置见[英文规范参考](/reference/configuration/providers/#mistral-images-per-request)。缺失、为空或非字符串的图片引用会在出站副本中替换为文本,且不占用图片限额。仅统计非空字符串,不验证 URL 语法或可访问性。支持 `image_url` 或 `image_url.url` 中的字符串。其他目标的行为保持不变。 + ## 自定义渠道的上下文窗口 自定义渠道未报告或配置模型窗口时,OpenCodex 依次使用 Azure 元数据、按模型 ID 查询的内置厂商/聚合元数据、已启用的 Context cap,最后回退到 128k。已有发现值与配置值取较小者,Context cap 仍会向下限制窗口,内置渠道行为不变。精确 ID 优先,大小写或点号/短横线变体仅在无冲突时匹配;未知或浮动别名不猜测。推断会影响 Codex 压缩预算和其他客户端目录(含 Claude 长上下文选择),但不作为中转实测能力供路由使用。同名模型不保证中转支持官方窗口;请用 modelContextWindows 或 Context cap 设置中转实际上限。不会自动开启 cap,也不会调用模型探测。OpenAI API 窗口不代表 Codex 订阅窗口。 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 80b14de4608..4a15544c6c0 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -5,6 +5,10 @@ description: 供應商項目、認證、端點、模型目錄、配額、context 供應商告訴 opencodex 模型在哪裡、它使用哪種 wire adapter,以及請求如何被認證。 +## Mistral 每次請求的圖片數量 + +Mistral 圖片數量限制及 `mistralMaxImages` 設定請參閱[英文規範參考](/reference/configuration/providers/#mistral-images-per-request)。缺少、空字串或非字串的圖片參照會在傳出副本中替換為文字,且不占用圖片限額。僅計算非空字串,不驗證 URL 語法或可存取性。支援 `image_url` 或 `image_url.url` 中的字串。其他目的地的行為維持不變。 + ## 自訂供應商的上下文視窗 自訂供應商未回報或設定模型視窗時,OpenCodex 依序使用 Azure 中繼資料、按模型 ID 查詢的內建廠商/聚合中繼資料、已啟用的 Context cap,最後回退至 128k。已發現值與設定值取較小者,Context cap 仍限制視窗,內建供應商行為不變。精確 ID 優先,大小寫或點號/連字號變體僅在無衝突時匹配;未知或浮動別名不猜測。推斷影響 Codex 壓縮預算及其他客戶端目錄(含 Claude 長上下文選擇),但不作為路由的實測能力。同名模型不保證中轉支援官方視窗;請用 modelContextWindows 或 Context cap 設定實際上限。不會自動啟用 cap,也不會呼叫模型探測。OpenAI API 視窗不代表 Codex 訂閱視窗。 From f48087ced527c2e8a9cac07c6ff14421d5890025 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2026 05:14:48 +0000 Subject: [PATCH 08/11] docs(mistral): qualify malformed image replacement scope Co-Authored-By: Epinephrine --- .../content/docs/reference/configuration/providers.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index d394583751d..f6f13ebd506 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -55,9 +55,12 @@ keeps them; any other model uses 8. Declare a model's own cap with 1000). The declaration outranks the registry row, and PATCH `null` on the axis removes it. Only `image_url` parts with a nonempty string in `image_url` or `image_url.url` count toward the cap; this check does not validate URL syntax or reachability. Parts with missing, empty, -or non-string image references are replaced with `[image omitted: missing or malformed image URL]` -in the outbound copy without consuming the image cap, even when the valid-image count is below -the cap. +or non-string image references that reach the limiter are replaced with +`[image omitted: missing or malformed image URL]` in the outbound copy without consuming the +image cap, even when the valid-image count is below the cap. For translated Responses requests, +the parser drops `input_image` blocks with no usable image URL or file ID before the limiter +runs, and a usable file ID becomes an `[image: ]` text marker instead of an image +reference. Pruning adds no separate upload or persistence. Images retained within the cap are sent to Mistral in the outbound request. Other destinations are unchanged. From 4b0f0bccdc5b7e70f14d0bf0d2bfd7b8e466aada Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Mon, 9 Nov 2026 14:34:12 +0900 Subject: [PATCH 09/11] docs: distinguish Mistral limiter replacement from Responses media refusal --- .../src/content/docs/fr/reference/configuration/providers.md | 2 +- .../src/content/docs/ja/reference/configuration/providers.md | 2 +- .../src/content/docs/ko/reference/configuration/providers.md | 2 +- .../src/content/docs/reference/configuration/providers.md | 4 ++-- .../src/content/docs/ru/reference/configuration/providers.md | 2 +- .../src/content/docs/tr/reference/configuration/providers.md | 2 +- .../content/docs/zh-cn/reference/configuration/providers.md | 2 +- .../content/docs/zh-tw/reference/configuration/providers.md | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index 0d51eb3250e..dc79288730a 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -8,7 +8,7 @@ authentifiées. ## Nombre d’images par requête Mistral -La limite d’images de Mistral et le réglage `mistralMaxImages` sont décrits dans la [référence canonique en anglais](/reference/configuration/providers/#mistral-images-per-request). Les références d’image absentes, vides ou non textuelles sont remplacées par du texte dans la copie sortante et ne consomment pas cette limite. Seules les chaînes non vides sont comptées ; la syntaxe et l’accessibilité des URL ne sont pas vérifiées. Les deux formes acceptées sont `image_url` et `image_url.url`. Les autres destinations restent inchangées. +La limite d’images de Mistral et le réglage `mistralMaxImages` sont décrits dans la [référence canonique en anglais](/reference/configuration/providers/#mistral-images-per-request). Pour les éléments `image_url` qui atteignent le limiteur, les références absentes, vides ou non textuelles sont remplacées par du texte dans la copie sortante sans consommer la limite. Lors de la traduction des requêtes Responses, le parseur omet les blocs `input_image` sans URL utilisable ni identifiant de fichier. Un `file_id` non vide sans `image_url` non vide entraîne le refus de la requête par la protection des médias avant l’envoi à Mistral ; aucun marqueur textuel contenant l’identifiant du fichier n’est envoyé. Le limiteur compte seulement les chaînes non vides dans `image_url` ou `image_url.url`, sans vérifier la syntaxe ni l’accessibilité des URL. Les autres destinations restent inchangées. ## Contexte des fournisseurs personnalisés diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index 38d3b55b42e..f09071fbdaa 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: プロバイダー エントリ、認証、エンドポイント、 ## Mistral のリクエストごとの画像数 -Mistral の画像数制限と `mistralMaxImages` の設定は[英語の正規リファレンス](/reference/configuration/providers/#mistral-images-per-request)を参照してください。画像参照が欠落、空、または文字列でない場合、送信用コピー内でテキストに置き換えられ、画像数の上限を消費しません。空でない文字列のみを数え、URL の構文や到達可能性は検証しません。 対象の文字列は `image_url` または `image_url.url` に指定します。他の送信先の動作は変わりません。 +Mistral の画像数制限と `mistralMaxImages` の設定は[英語の正規リファレンス](/reference/configuration/providers/#mistral-images-per-request)を参照してください。リミッターに届いた `image_url` 要素の画像参照が欠落、空、または文字列でない場合、送信用コピー内でテキストに置き換えられ、画像数の上限を消費しません。変換される Responses リクエストでは、有効な URL もファイル ID もない `input_image` をパーサーが省略します。空でない `file_id` があり、空でない `image_url` がない場合、メディアガードが Mistral への送信前にリクエストを拒否し、ファイル ID のテキストマーカーは送信しません。リミッターは `image_url` または `image_url.url` の空でない文字列のみを数え、URL の構文や到達可能性は検証しません。他の送信先の動作は変わりません。 ## カスタムプロバイダーのコンテキスト diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index 4aebfa94372..fdbfa438ddb 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: 공급자 항목, 인증, 엔드포인트, 모델 카탈로그, 할 ## Mistral 요청당 이미지 수 -Mistral 요청의 이미지 수 제한과 `mistralMaxImages` 설정은 [영문 기준 문서](/reference/configuration/providers/#mistral-images-per-request)를 참조하세요. 누락되거나 비어 있거나 문자열이 아닌 이미지 참조는 전송용 사본에서 텍스트로 대체되며 이미지 한도를 소비하지 않습니다. 비어 있지 않은 문자열만 계산하고 URL 문법이나 접근 가능성은 검증하지 않습니다. 문자열은 `image_url` 또는 `image_url.url`에 지정합니다. 다른 대상의 동작은 바뀌지 않습니다. +Mistral 요청의 이미지 수 제한과 `mistralMaxImages` 설정은 [영문 기준 문서](/reference/configuration/providers/#mistral-images-per-request)를 참조하세요. 제한기에 도달한 `image_url` 항목의 이미지 참조가 누락되거나 비어 있거나 문자열이 아니면 전송용 사본에서 텍스트로 대체되며 이미지 한도를 소비하지 않습니다. 번역되는 Responses 요청에서 사용 가능한 URL과 파일 ID가 모두 없는 `input_image`는 파서가 제외합니다. 비어 있지 않은 `file_id`만 있고 비어 있지 않은 `image_url`이 없으면 미디어 가드가 Mistral 전송 전에 요청을 거절하며 파일 ID 텍스트 마커를 보내지 않습니다. 제한기는 `image_url` 또는 `image_url.url`의 비어 있지 않은 문자열만 계산하고 URL 문법이나 접근 가능성은 검증하지 않습니다. 다른 대상의 동작은 바뀌지 않습니다. ## 사용자 지정 공급자의 컨텍스트 diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index f6f13ebd506..4f9064846e1 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -59,8 +59,8 @@ or non-string image references that reach the limiter are replaced with `[image omitted: missing or malformed image URL]` in the outbound copy without consuming the image cap, even when the valid-image count is below the cap. For translated Responses requests, the parser drops `input_image` blocks with no usable image URL or file ID before the limiter -runs, and a usable file ID becomes an `[image: ]` text marker instead of an image -reference. +runs. A nonempty `file_id` without a nonempty `image_url` is refused by the translated-adapter +media guard before Mistral dispatch: it is not sent as an `[image: ]` text marker. Pruning adds no separate upload or persistence. Images retained within the cap are sent to Mistral in the outbound request. Other destinations are unchanged. diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index e92407e1d9e..d1f211964ff 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -8,7 +8,7 @@ description: Записи провайдеров, аутентификация, ## Число изображений в запросе Mistral -Ограничение числа изображений Mistral и настройка `mistralMaxImages` описаны в [каноническом справочнике на английском языке](/reference/configuration/providers/#mistral-images-per-request). Отсутствующие, пустые или нестроковые ссылки на изображения заменяются текстом в исходящей копии и не расходуют лимит изображений. Учитываются только непустые строки; синтаксис и доступность URL не проверяются. Поддерживаются строки в `image_url` или `image_url.url`. Поведение других назначений не меняется. +Ограничение числа изображений Mistral и настройка `mistralMaxImages` описаны в [каноническом справочнике на английском языке](/reference/configuration/providers/#mistral-images-per-request). В элементах `image_url`, дошедших до ограничителя, отсутствующие, пустые или нестроковые ссылки заменяются текстом в исходящей копии и не расходуют лимит. При преобразовании запросов Responses парсер исключает `input_image` без пригодного URL и идентификатора файла. Если есть непустой `file_id`, но нет непустого `image_url`, проверка медиа отклоняет запрос до отправки в Mistral; текстовый маркер с идентификатором файла не отправляется. Ограничитель учитывает только непустые строки в `image_url` или `image_url.url`; синтаксис и доступность URL не проверяются. Поведение других назначений не меняется. ## Контекст пользовательских провайдеров diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index c212f477866..c3a6741d50e 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -8,7 +8,7 @@ konuştuğunu ve isteklerin nasıl doğrulandığını söyler. ## Mistral isteği başına görüntü sayısı -Mistral görüntü sınırı ve `mistralMaxImages` ayarı için [İngilizce temel başvuruya](/reference/configuration/providers/#mistral-images-per-request) bakın. Eksik, boş veya dize olmayan görüntü başvuruları, giden kopyada metinle değiştirilir ve görüntü sınırını tüketmez. Yalnızca boş olmayan dizeler sayılır; URL sözdizimi veya erişilebilirliği doğrulanmaz. Dizeler `image_url` veya `image_url.url` içinde olabilir. Diğer hedeflerin davranışı değişmez. +Mistral görüntü sınırı ve `mistralMaxImages` ayarı için [İngilizce temel başvuruya](/reference/configuration/providers/#mistral-images-per-request) bakın. Sınırlayıcıya ulaşan `image_url` öğelerinde eksik, boş veya dize olmayan başvurular giden kopyada metinle değiştirilir ve sınırı tüketmez. Çevrilen Responses isteklerinde kullanılabilir URL veya dosya kimliği olmayan `input_image` blokları ayrıştırıcı tarafından atılır. Boş olmayan bir `file_id` bulunup boş olmayan bir `image_url` yoksa medya koruması isteği Mistral gönderiminden önce reddeder; dosya kimliği metin işareti gönderilmez. Sınırlayıcı yalnızca `image_url` veya `image_url.url` içindeki boş olmayan dizeleri sayar; URL sözdizimi veya erişilebilirliği doğrulanmaz. Diğer hedeflerin davranışı değişmez. ## Özel sağlayıcılarda bağlam penceresi diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index 4141519338c..45804c53a19 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: 提供者条目、身份验证、端点、模型目录、配额、 ## Mistral 每次请求的图片数量 -Mistral 图片数量限制及 `mistralMaxImages` 设置见[英文规范参考](/reference/configuration/providers/#mistral-images-per-request)。缺失、为空或非字符串的图片引用会在出站副本中替换为文本,且不占用图片限额。仅统计非空字符串,不验证 URL 语法或可访问性。支持 `image_url` 或 `image_url.url` 中的字符串。其他目标的行为保持不变。 +Mistral 图片数量限制及 `mistralMaxImages` 设置见[英文规范参考](/reference/configuration/providers/#mistral-images-per-request)。对于到达限制器的 `image_url` 项,缺失、为空或非字符串的图片引用会在出站副本中替换为文本,且不占用图片限额。对于经过转换的 Responses 请求,解析器会丢弃既没有可用 URL 也没有文件 ID 的 `input_image`。若存在非空 `file_id` 而没有非空 `image_url`,媒体防护会在发送到 Mistral 前拒绝请求,不会发送包含文件 ID 的文本标记。限制器仅统计 `image_url` 或 `image_url.url` 中的非空字符串,不验证 URL 语法或可访问性。其他目标的行为保持不变。 ## 自定义渠道的上下文窗口 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 4a15544c6c0..12acb4c284d 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: 供應商項目、認證、端點、模型目錄、配額、context ## Mistral 每次請求的圖片數量 -Mistral 圖片數量限制及 `mistralMaxImages` 設定請參閱[英文規範參考](/reference/configuration/providers/#mistral-images-per-request)。缺少、空字串或非字串的圖片參照會在傳出副本中替換為文字,且不占用圖片限額。僅計算非空字串,不驗證 URL 語法或可存取性。支援 `image_url` 或 `image_url.url` 中的字串。其他目的地的行為維持不變。 +Mistral 圖片數量限制及 `mistralMaxImages` 設定請參閱[英文規範參考](/reference/configuration/providers/#mistral-images-per-request)。對於到達限制器的 `image_url` 項目,缺少、空字串或非字串的圖片參照會在傳出副本中替換為文字,且不占用圖片限額。對於經過轉換的 Responses 請求,剖析器會省略既沒有可用 URL 也沒有檔案 ID 的 `input_image`。若有非空 `file_id` 而沒有非空 `image_url`,媒體防護會在傳送至 Mistral 前拒絕請求,不會傳送包含檔案 ID 的文字標記。限制器僅計算 `image_url` 或 `image_url.url` 中的非空字串,不驗證 URL 語法或可存取性。其他目的地的行為維持不變。 ## 自訂供應商的上下文視窗 From a7114b6e19829a935785b1cb14fd0935b607eff2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2026 06:00:17 +0000 Subject: [PATCH 10/11] docs(mistral): keep providers reference under the size-ratchet threshold Merging dev pushed the merged providers.md to 2004 lines, tripping the file-size ratchet's NEW_OVERSIZED verdict. Condense the image-limit note to two lines (same content) so the merged file stays below 2000. Co-Authored-By: Epinephrine --- .../content/docs/reference/configuration/providers.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 641665e51b6..94dd97ac002 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -53,14 +53,7 @@ any other. The measured caps ship as registry keeps them; any other model uses 8. Declare a model's own cap with `modelCapabilities: { "mistral-small-2603": { mistralMaxImages: 8 } }` (an integer from 1 to 1000). The declaration outranks the registry row, and PATCH `null` on the axis removes it. -Only `image_url` parts with a nonempty string in `image_url` or `image_url.url` count toward -the cap; this check does not validate URL syntax or reachability. Parts with missing, empty, -or non-string image references that reach the limiter are replaced with -`[image omitted: missing or malformed image URL]` in the outbound copy without consuming the -image cap, even when the valid-image count is below the cap. For translated Responses requests, -the parser drops `input_image` blocks with no usable image URL or file ID before the limiter -runs. A nonempty `file_id` without a nonempty `image_url` is refused by the translated-adapter -media guard before Mistral dispatch: it is not sent as an `[image: ]` text marker. +Only `image_url` parts with a nonempty string in `image_url` or `image_url.url` count toward the cap; this check does not validate URL syntax or reachability. Parts with missing, empty, or non-string image references that reach the limiter are replaced with `[image omitted: missing or malformed image URL]` in the outbound copy without consuming the cap, even when the valid-image count is below the cap. For translated Responses requests, the parser drops `input_image` blocks with no usable image URL or file ID before the limiter runs. A nonempty `file_id` without a nonempty `image_url` is refused by the translated-adapter media guard before Mistral dispatch: it is not sent as an `[image: ]` text marker. Pruning adds no separate upload or persistence. Images retained within the cap are sent to Mistral in the outbound request. Other destinations are unchanged. From 259d7471cabb849ddbc59d3cd11fd117ec43bc1b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2026 06:19:07 +0000 Subject: [PATCH 11/11] docs(mistral): scope file-ID refusal to positions the media guard scans Devin Review flagged the new Mistral image-cap paragraph: it claimed a file-ID-only input_image is always refused by the translated-adapter media guard, but the guard only inspects message content, tool output and top-level input blocks. A replayed agent_message is not scanned, so the parser degrades a file-ID-only input_image inside it to an [image: ] text marker that reaches Mistral as ordinary text. Scope the refusal claim to scanned positions and describe the agent_message marker path in all eight locales. Co-Authored-By: Epinephrine --- .../src/content/docs/fr/reference/configuration/providers.md | 2 +- .../src/content/docs/ja/reference/configuration/providers.md | 2 +- .../src/content/docs/ko/reference/configuration/providers.md | 2 +- docs-site/src/content/docs/reference/configuration/providers.md | 2 +- .../src/content/docs/ru/reference/configuration/providers.md | 2 +- .../src/content/docs/tr/reference/configuration/providers.md | 2 +- .../src/content/docs/zh-cn/reference/configuration/providers.md | 2 +- .../src/content/docs/zh-tw/reference/configuration/providers.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index dc79288730a..c4c8241454d 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -8,7 +8,7 @@ authentifiées. ## Nombre d’images par requête Mistral -La limite d’images de Mistral et le réglage `mistralMaxImages` sont décrits dans la [référence canonique en anglais](/reference/configuration/providers/#mistral-images-per-request). Pour les éléments `image_url` qui atteignent le limiteur, les références absentes, vides ou non textuelles sont remplacées par du texte dans la copie sortante sans consommer la limite. Lors de la traduction des requêtes Responses, le parseur omet les blocs `input_image` sans URL utilisable ni identifiant de fichier. Un `file_id` non vide sans `image_url` non vide entraîne le refus de la requête par la protection des médias avant l’envoi à Mistral ; aucun marqueur textuel contenant l’identifiant du fichier n’est envoyé. Le limiteur compte seulement les chaînes non vides dans `image_url` ou `image_url.url`, sans vérifier la syntaxe ni l’accessibilité des URL. Les autres destinations restent inchangées. +La limite d’images de Mistral et le réglage `mistralMaxImages` sont décrits dans la [référence canonique en anglais](/reference/configuration/providers/#mistral-images-per-request). Pour les éléments `image_url` qui atteignent le limiteur, les références absentes, vides ou non textuelles sont remplacées par du texte dans la copie sortante sans consommer la limite. Lors de la traduction des requêtes Responses, le parseur omet les blocs `input_image` sans URL utilisable ni identifiant de fichier. Dans un bloc de message ou de sortie d’outil, un `file_id` non vide sans `image_url` non vide entraîne le refus de la requête par la protection des médias avant l’envoi à Mistral ; dans un `agent_message` rejoué, que la protection n’inspecte pas, le parseur émet à la place un marqueur textuel `[image: ]` envoyé comme texte ordinaire sans consommer la limite d’images. Le limiteur compte seulement les chaînes non vides dans `image_url` ou `image_url.url`, sans vérifier la syntaxe ni l’accessibilité des URL. Les autres destinations restent inchangées. ## Contexte des fournisseurs personnalisés diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index c206b1cf1de..a138683925a 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: プロバイダー エントリ、認証、エンドポイント、 ## Mistral のリクエストごとの画像数 -Mistral の画像数制限と `mistralMaxImages` の設定は[英語の正規リファレンス](/reference/configuration/providers/#mistral-images-per-request)を参照してください。リミッターに届いた `image_url` 要素の画像参照が欠落、空、または文字列でない場合、送信用コピー内でテキストに置き換えられ、画像数の上限を消費しません。変換される Responses リクエストでは、有効な URL もファイル ID もない `input_image` をパーサーが省略します。空でない `file_id` があり、空でない `image_url` がない場合、メディアガードが Mistral への送信前にリクエストを拒否し、ファイル ID のテキストマーカーは送信しません。リミッターは `image_url` または `image_url.url` の空でない文字列のみを数え、URL の構文や到達可能性は検証しません。他の送信先の動作は変わりません。 +Mistral の画像数制限と `mistralMaxImages` の設定は[英語の正規リファレンス](/reference/configuration/providers/#mistral-images-per-request)を参照してください。リミッターに届いた `image_url` 要素の画像参照が欠落、空、または文字列でない場合、送信用コピー内でテキストに置き換えられ、画像数の上限を消費しません。変換される Responses リクエストでは、有効な URL もファイル ID もない `input_image` をパーサーが省略します。メッセージやツール出力のブロック内で、空でない `file_id` があり空でない `image_url` がない場合、メディアガードが Mistral への送信前にリクエストを拒否します。ガードが検査しない再生済みの `agent_message` 内では、パーサーが代わりに `[image: ]` テキストマーカーを出力し、通常のテキストとして送信され画像数の上限を消費しません。リミッターは `image_url` または `image_url.url` の空でない文字列のみを数え、URL の構文や到達可能性は検証しません。他の送信先の動作は変わりません。 ## カスタムプロバイダーのコンテキスト diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index a06d960875d..d05f2044ebc 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: 공급자 항목, 인증, 엔드포인트, 모델 카탈로그, 할 ## Mistral 요청당 이미지 수 -Mistral 요청의 이미지 수 제한과 `mistralMaxImages` 설정은 [영문 기준 문서](/reference/configuration/providers/#mistral-images-per-request)를 참조하세요. 제한기에 도달한 `image_url` 항목의 이미지 참조가 누락되거나 비어 있거나 문자열이 아니면 전송용 사본에서 텍스트로 대체되며 이미지 한도를 소비하지 않습니다. 번역되는 Responses 요청에서 사용 가능한 URL과 파일 ID가 모두 없는 `input_image`는 파서가 제외합니다. 비어 있지 않은 `file_id`만 있고 비어 있지 않은 `image_url`이 없으면 미디어 가드가 Mistral 전송 전에 요청을 거절하며 파일 ID 텍스트 마커를 보내지 않습니다. 제한기는 `image_url` 또는 `image_url.url`의 비어 있지 않은 문자열만 계산하고 URL 문법이나 접근 가능성은 검증하지 않습니다. 다른 대상의 동작은 바뀌지 않습니다. +Mistral 요청의 이미지 수 제한과 `mistralMaxImages` 설정은 [영문 기준 문서](/reference/configuration/providers/#mistral-images-per-request)를 참조하세요. 제한기에 도달한 `image_url` 항목의 이미지 참조가 누락되거나 비어 있거나 문자열이 아니면 전송용 사본에서 텍스트로 대체되며 이미지 한도를 소비하지 않습니다. 번역되는 Responses 요청에서 사용 가능한 URL과 파일 ID가 모두 없는 `input_image`는 파서가 제외합니다. 메시지나 도구 출력 블록 안에서 비어 있지 않은 `file_id`만 있고 비어 있지 않은 `image_url`이 없으면 미디어 가드가 Mistral 전송 전에 요청을 거절합니다. 가드가 검사하지 않는 재생된 `agent_message` 내부에서는 파서가 대신 `[image: ]` 텍스트 마커를 내보내며 일반 텍스트로 전송되고 이미지 한도를 소비하지 않습니다. 제한기는 `image_url` 또는 `image_url.url`의 비어 있지 않은 문자열만 계산하고 URL 문법이나 접근 가능성은 검증하지 않습니다. 다른 대상의 동작은 바뀌지 않습니다. ## 사용자 지정 공급자의 컨텍스트 diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 94dd97ac002..fbf43eadd0a 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -53,7 +53,7 @@ any other. The measured caps ship as registry keeps them; any other model uses 8. Declare a model's own cap with `modelCapabilities: { "mistral-small-2603": { mistralMaxImages: 8 } }` (an integer from 1 to 1000). The declaration outranks the registry row, and PATCH `null` on the axis removes it. -Only `image_url` parts with a nonempty string in `image_url` or `image_url.url` count toward the cap; this check does not validate URL syntax or reachability. Parts with missing, empty, or non-string image references that reach the limiter are replaced with `[image omitted: missing or malformed image URL]` in the outbound copy without consuming the cap, even when the valid-image count is below the cap. For translated Responses requests, the parser drops `input_image` blocks with no usable image URL or file ID before the limiter runs. A nonempty `file_id` without a nonempty `image_url` is refused by the translated-adapter media guard before Mistral dispatch: it is not sent as an `[image: ]` text marker. +Only `image_url` parts with a nonempty string in `image_url` or `image_url.url` count toward the cap; this check does not validate URL syntax or reachability. Parts with missing, empty, or non-string image references that reach the limiter are replaced with `[image omitted: missing or malformed image URL]` in the outbound copy without consuming the cap, even when the valid-image count is below the cap. For translated Responses requests, the parser drops `input_image` blocks with no usable image URL or file ID before the limiter runs. A nonempty `file_id` without a nonempty `image_url` in message content or tool output is refused by the translated-adapter media guard before Mistral dispatch; inside a replayed `agent_message`, whose content the guard does not inspect, the parser instead emits an `[image: ]` text marker that travels as ordinary text and does not consume the image cap. Pruning adds no separate upload or persistence. Images retained within the cap are sent to Mistral in the outbound request. Other destinations are unchanged. diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index d1f211964ff..93c92cc44cc 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -8,7 +8,7 @@ description: Записи провайдеров, аутентификация, ## Число изображений в запросе Mistral -Ограничение числа изображений Mistral и настройка `mistralMaxImages` описаны в [каноническом справочнике на английском языке](/reference/configuration/providers/#mistral-images-per-request). В элементах `image_url`, дошедших до ограничителя, отсутствующие, пустые или нестроковые ссылки заменяются текстом в исходящей копии и не расходуют лимит. При преобразовании запросов Responses парсер исключает `input_image` без пригодного URL и идентификатора файла. Если есть непустой `file_id`, но нет непустого `image_url`, проверка медиа отклоняет запрос до отправки в Mistral; текстовый маркер с идентификатором файла не отправляется. Ограничитель учитывает только непустые строки в `image_url` или `image_url.url`; синтаксис и доступность URL не проверяются. Поведение других назначений не меняется. +Ограничение числа изображений Mistral и настройка `mistralMaxImages` описаны в [каноническом справочнике на английском языке](/reference/configuration/providers/#mistral-images-per-request). В элементах `image_url`, дошедших до ограничителя, отсутствующие, пустые или нестроковые ссылки заменяются текстом в исходящей копии и не расходуют лимит. При преобразовании запросов Responses парсер исключает `input_image` без пригодного URL и идентификатора файла. В блоке сообщения или результата инструмента непустой `file_id` без непустого `image_url` приводит к отклонению запроса проверкой медиа до отправки в Mistral; внутри воспроизведённого `agent_message`, который проверка не инспектирует, парсер вместо этого выдаёт текстовый маркер `[image: ]`, отправляемый как обычный текст и не расходующий лимит изображений. Ограничитель учитывает только непустые строки в `image_url` или `image_url.url`; синтаксис и доступность URL не проверяются. Поведение других назначений не меняется. ## Контекст пользовательских провайдеров diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index c3a6741d50e..c8db68a57eb 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -8,7 +8,7 @@ konuştuğunu ve isteklerin nasıl doğrulandığını söyler. ## Mistral isteği başına görüntü sayısı -Mistral görüntü sınırı ve `mistralMaxImages` ayarı için [İngilizce temel başvuruya](/reference/configuration/providers/#mistral-images-per-request) bakın. Sınırlayıcıya ulaşan `image_url` öğelerinde eksik, boş veya dize olmayan başvurular giden kopyada metinle değiştirilir ve sınırı tüketmez. Çevrilen Responses isteklerinde kullanılabilir URL veya dosya kimliği olmayan `input_image` blokları ayrıştırıcı tarafından atılır. Boş olmayan bir `file_id` bulunup boş olmayan bir `image_url` yoksa medya koruması isteği Mistral gönderiminden önce reddeder; dosya kimliği metin işareti gönderilmez. Sınırlayıcı yalnızca `image_url` veya `image_url.url` içindeki boş olmayan dizeleri sayar; URL sözdizimi veya erişilebilirliği doğrulanmaz. Diğer hedeflerin davranışı değişmez. +Mistral görüntü sınırı ve `mistralMaxImages` ayarı için [İngilizce temel başvuruya](/reference/configuration/providers/#mistral-images-per-request) bakın. Sınırlayıcıya ulaşan `image_url` öğelerinde eksik, boş veya dize olmayan başvurular giden kopyada metinle değiştirilir ve sınırı tüketmez. Çevrilen Responses isteklerinde kullanılabilir URL veya dosya kimliği olmayan `input_image` blokları ayrıştırıcı tarafından atılır. Bir ileti ya da araç çıktısı bloğunda boş olmayan `file_id` bulunup boş olmayan `image_url` yoksa medya koruması isteği Mistral gönderiminden önce reddeder; korumanın incelemediği yeniden oynatılmış bir `agent_message` içinde ise ayrıştırıcı bunun yerine `[image: ]` metin işareti üretir ve bu işaret normal metin olarak gönderilip görüntü sınırını tüketmez. Sınırlayıcı yalnızca `image_url` veya `image_url.url` içindeki boş olmayan dizeleri sayar; URL sözdizimi veya erişilebilirliği doğrulanmaz. Diğer hedeflerin davranışı değişmez. ## Özel sağlayıcılarda bağlam penceresi diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index 82df2458667..1bd910af15d 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: 提供者条目、身份验证、端点、模型目录、配额、 ## Mistral 每次请求的图片数量 -Mistral 图片数量限制及 `mistralMaxImages` 设置见[英文规范参考](/reference/configuration/providers/#mistral-images-per-request)。对于到达限制器的 `image_url` 项,缺失、为空或非字符串的图片引用会在出站副本中替换为文本,且不占用图片限额。对于经过转换的 Responses 请求,解析器会丢弃既没有可用 URL 也没有文件 ID 的 `input_image`。若存在非空 `file_id` 而没有非空 `image_url`,媒体防护会在发送到 Mistral 前拒绝请求,不会发送包含文件 ID 的文本标记。限制器仅统计 `image_url` 或 `image_url.url` 中的非空字符串,不验证 URL 语法或可访问性。其他目标的行为保持不变。 +Mistral 图片数量限制及 `mistralMaxImages` 设置见[英文规范参考](/reference/configuration/providers/#mistral-images-per-request)。对于到达限制器的 `image_url` 项,缺失、为空或非字符串的图片引用会在出站副本中替换为文本,且不占用图片限额。对于经过转换的 Responses 请求,解析器会丢弃既没有可用 URL 也没有文件 ID 的 `input_image`。在消息或工具输出块中,若存在非空 `file_id` 而没有非空 `image_url`,媒体防护会在发送到 Mistral 前拒绝请求;在防护不检查的重放 `agent_message` 内,解析器会改为输出 `[image: ]` 文本标记,作为普通文本发送且不占用图片限额。限制器仅统计 `image_url` 或 `image_url.url` 中的非空字符串,不验证 URL 语法或可访问性。其他目标的行为保持不变。 ## 自定义渠道的上下文窗口 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 12acb4c284d..eb5a8fde79f 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -7,7 +7,7 @@ description: 供應商項目、認證、端點、模型目錄、配額、context ## Mistral 每次請求的圖片數量 -Mistral 圖片數量限制及 `mistralMaxImages` 設定請參閱[英文規範參考](/reference/configuration/providers/#mistral-images-per-request)。對於到達限制器的 `image_url` 項目,缺少、空字串或非字串的圖片參照會在傳出副本中替換為文字,且不占用圖片限額。對於經過轉換的 Responses 請求,剖析器會省略既沒有可用 URL 也沒有檔案 ID 的 `input_image`。若有非空 `file_id` 而沒有非空 `image_url`,媒體防護會在傳送至 Mistral 前拒絕請求,不會傳送包含檔案 ID 的文字標記。限制器僅計算 `image_url` 或 `image_url.url` 中的非空字串,不驗證 URL 語法或可存取性。其他目的地的行為維持不變。 +Mistral 圖片數量限制及 `mistralMaxImages` 設定請參閱[英文規範參考](/reference/configuration/providers/#mistral-images-per-request)。對於到達限制器的 `image_url` 項目,缺少、空字串或非字串的圖片參照會在傳出副本中替換為文字,且不占用圖片限額。對於經過轉換的 Responses 請求,剖析器會省略既沒有可用 URL 也沒有檔案 ID 的 `input_image`。在訊息或工具輸出區塊中,若有非空 `file_id` 而沒有非空 `image_url`,媒體防護會在傳送至 Mistral 前拒絕請求;在防護不檢查的重放 `agent_message` 內,剖析器會改為輸出 `[image: ]` 文字標記,作為一般文字傳送且不占用圖片限額。限制器僅計算 `image_url` 或 `image_url.url` 中的非空字串,不驗證 URL 語法或可存取性。其他目的地的行為維持不變。 ## 自訂供應商的上下文視窗