feat: add OpenRouter quantization filter to exclude low-bit providers

Adds an opt-in checkbox to filter out low-bit quantization providers
(FP4/FP6/Int4) when using OpenRouter, preventing broken CJK encoding.

- Add openRouterExcludeLowQuantization to provider settings schema
- Add buildProviderOptions() method to centralize provider options
- Add UI checkbox in OpenRouter settings
- Add i18n translations for all 18 locales
- Add 6 tests for buildProviderOptions behavior

Closes #11325
This commit is contained in:
Roo Code 2026-02-24 02:08:59 +00:00
parent aca95ccd04
commit 841011d96f
22 changed files with 201 additions and 18 deletions

View file

@ -206,6 +206,7 @@ const openRouterSchema = baseProviderSettingsSchema.extend({
openRouterModelId: z.string().optional(),
openRouterBaseUrl: z.string().optional(),
openRouterSpecificProvider: z.string().optional(),
openRouterExcludeLowQuantization: z.boolean().optional(),
})
const bedrockSchema = apiModelIdProviderModelSchema.extend({

View file

@ -698,4 +698,77 @@ describe("OpenRouterHandler", () => {
)
})
})
describe("buildProviderOptions", () => {
it("returns undefined when no specific provider and no quantization filter", () => {
const handler = new OpenRouterHandler({
openRouterApiKey: "test-key",
openRouterModelId: "anthropic/claude-sonnet-4",
})
const result = (handler as any).buildProviderOptions()
expect(result).toBeUndefined()
})
it("returns provider routing when specific provider is set", () => {
const handler = new OpenRouterHandler({
openRouterApiKey: "test-key",
openRouterModelId: "anthropic/claude-sonnet-4",
openRouterSpecificProvider: "Anthropic",
})
const result = (handler as any).buildProviderOptions()
expect(result).toEqual({
order: ["Anthropic"],
only: ["Anthropic"],
allow_fallbacks: false,
})
})
it("returns quantizations when excludeLowQuantization is enabled", () => {
const handler = new OpenRouterHandler({
openRouterApiKey: "test-key",
openRouterModelId: "anthropic/claude-sonnet-4",
openRouterExcludeLowQuantization: true,
})
const result = (handler as any).buildProviderOptions()
expect(result).toEqual({
quantizations: ["fp16", "bf16", "fp8", "int8"],
})
})
it("combines specific provider and quantization filter", () => {
const handler = new OpenRouterHandler({
openRouterApiKey: "test-key",
openRouterModelId: "anthropic/claude-sonnet-4",
openRouterSpecificProvider: "Anthropic",
openRouterExcludeLowQuantization: true,
})
const result = (handler as any).buildProviderOptions()
expect(result).toEqual({
order: ["Anthropic"],
only: ["Anthropic"],
allow_fallbacks: false,
quantizations: ["fp16", "bf16", "fp8", "int8"],
})
})
it("returns undefined when specific provider is the default", () => {
const handler = new OpenRouterHandler({
openRouterApiKey: "test-key",
openRouterModelId: "anthropic/claude-sonnet-4",
openRouterSpecificProvider: "[default]",
})
const result = (handler as any).buildProviderOptions()
expect(result).toBeUndefined()
})
it("returns undefined when excludeLowQuantization is false", () => {
const handler = new OpenRouterHandler({
openRouterApiKey: "test-key",
openRouterModelId: "anthropic/claude-sonnet-4",
openRouterExcludeLowQuantization: false,
})
const result = (handler as any).buildProviderOptions()
expect(result).toBeUndefined()
})
})
})

View file

@ -317,15 +317,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
messages: openAiMessages,
stream: true,
stream_options: { include_usage: true },
// Only include provider if openRouterSpecificProvider is not "[default]".
...(this.options.openRouterSpecificProvider &&
this.options.openRouterSpecificProvider !== OPENROUTER_DEFAULT_PROVIDER_NAME && {
provider: {
order: [this.options.openRouterSpecificProvider],
only: [this.options.openRouterSpecificProvider],
allow_fallbacks: false,
},
}),
...(this.buildProviderOptions() && { provider: this.buildProviderOptions() }),
...(reasoning && { reasoning }),
tools: this.convertToolsForOpenAI(metadata?.tools),
tool_choice: metadata?.tool_choice,
@ -574,6 +566,47 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
return { id, info, topP: isDeepSeekR1 ? 0.95 : undefined, ...params }
}
/**
* Build the `provider` options object for OpenRouter requests.
* Combines specific provider routing and quantization filtering.
*/
private buildProviderOptions():
| {
order?: string[]
only?: string[]
allow_fallbacks?: boolean
quantizations?: string[]
}
| undefined {
const hasSpecificProvider =
this.options.openRouterSpecificProvider &&
this.options.openRouterSpecificProvider !== OPENROUTER_DEFAULT_PROVIDER_NAME
const excludeLowQuantization = this.options.openRouterExcludeLowQuantization
if (!hasSpecificProvider && !excludeLowQuantization) {
return undefined
}
const provider: {
order?: string[]
only?: string[]
allow_fallbacks?: boolean
quantizations?: string[]
} = {}
if (hasSpecificProvider) {
provider.order = [this.options.openRouterSpecificProvider!]
provider.only = [this.options.openRouterSpecificProvider!]
provider.allow_fallbacks = false
}
if (excludeLowQuantization) {
provider.quantizations = ["fp16", "bf16", "fp8", "int8"]
}
return provider
}
async completePrompt(prompt: string) {
let { id: modelId, maxTokens, temperature, reasoning } = await this.fetchModel()
@ -583,15 +616,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
temperature,
messages: [{ role: "user", content: prompt }],
stream: false,
// Only include provider if openRouterSpecificProvider is not "[default]".
...(this.options.openRouterSpecificProvider &&
this.options.openRouterSpecificProvider !== OPENROUTER_DEFAULT_PROVIDER_NAME && {
provider: {
order: [this.options.openRouterSpecificProvider],
only: [this.options.openRouterSpecificProvider],
allow_fallbacks: false,
},
}),
...(this.buildProviderOptions() && { provider: this.buildProviderOptions() }),
...(reasoning && { reasoning }),
}

View file

@ -103,6 +103,18 @@ export const OpenRouter = ({
)}
</div>
)}
<div>
<Checkbox
checked={apiConfiguration?.openRouterExcludeLowQuantization ?? false}
onChange={(checked: boolean) => {
setApiConfigurationField("openRouterExcludeLowQuantization", checked)
}}>
{t("settings:providers.openRouter.excludeLowQuantization.label")}
</Checkbox>
<div className="text-sm text-vscode-descriptionForeground mt-1 ml-6">
{t("settings:providers.openRouter.excludeLowQuantization.description")}
</div>
</div>
<ModelPicker
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}

View file

@ -477,6 +477,10 @@
"title": "Encaminament de Proveïdors d'OpenRouter",
"description": "OpenRouter dirigeix les sol·licituds als millors proveïdors disponibles per al vostre model. Per defecte, les sol·licituds s'equilibren entre els principals proveïdors per maximitzar el temps de funcionament. No obstant això, podeu triar un proveïdor específic per utilitzar amb aquest model.",
"learnMore": "Més informació sobre l'encaminament de proveïdors"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter Anbieter-Routing",
"description": "OpenRouter leitet Anfragen an die besten verfügbaren Anbieter für dein Modell weiter. Standardmäßig werden Anfragen über die Top-Anbieter lastverteilt, um maximale Verfügbarkeit zu gewährleisten. Du kannst jedoch einen bestimmten Anbieter für dieses Modell auswählen.",
"learnMore": "Mehr über Anbieter-Routing erfahren"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -540,6 +540,10 @@
"title": "OpenRouter Provider Routing",
"description": "OpenRouter routes requests to the best available providers for your model. By default, requests are load balanced across the top providers to maximize uptime. However, you can choose a specific provider to use for this model.",
"learnMore": "Learn more about provider routing"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Enrutamiento de Proveedores de OpenRouter",
"description": "OpenRouter dirige las solicitudes a los mejores proveedores disponibles para su modelo. Por defecto, las solicitudes se equilibran entre los principales proveedores para maximizar el tiempo de actividad. Sin embargo, puede elegir un proveedor específico para este modelo.",
"learnMore": "Más información sobre el enrutamiento de proveedores"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Routage des fournisseurs OpenRouter",
"description": "OpenRouter dirige les requêtes vers les meilleurs fournisseurs disponibles pour votre modèle. Par défaut, les requêtes sont équilibrées entre les principaux fournisseurs pour maximiser la disponibilité. Cependant, vous pouvez choisir un fournisseur spécifique à utiliser pour ce modèle.",
"learnMore": "En savoir plus sur le routage des fournisseurs"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter प्रदाता रूटिंग",
"description": "OpenRouter आपके मॉडल के लिए सर्वोत्तम उपलब्ध प्रदाताओं को अनुरोध भेजता है। डिफ़ॉल्ट रूप से, अपटाइम को अधिकतम करने के लिए अनुरोधों को शीर्ष प्रदाताओं के बीच संतुलित किया जाता है। हालांकि, आप इस मॉडल के लिए उपयोग करने के लिए एक विशिष्ट प्रदाता चुन सकते हैं।",
"learnMore": "प्रदाता रूटिंग के बारे में अधिक जानें"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter Provider Routing",
"description": "OpenRouter mengarahkan permintaan ke provider terbaik yang tersedia untuk model kamu. Secara default, permintaan diseimbangkan beban di seluruh provider teratas untuk memaksimalkan uptime. Namun, kamu dapat memilih provider spesifik untuk digunakan untuk model ini.",
"learnMore": "Pelajari lebih lanjut tentang provider routing"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Routing dei fornitori OpenRouter",
"description": "OpenRouter indirizza le richieste ai migliori fornitori disponibili per il tuo modello. Per impostazione predefinita, le richieste sono bilanciate tra i principali fornitori per massimizzare il tempo di attività. Tuttavia, puoi scegliere un fornitore specifico da utilizzare per questo modello.",
"learnMore": "Scopri di più sul routing dei fornitori"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouterプロバイダールーティング",
"description": "OpenRouterはあなたのモデルに最適な利用可能なプロバイダーにリクエストを転送します。デフォルトでは、稼働時間を最大化するために、リクエストはトッププロバイダー間でロードバランスされます。ただし、このモデルに使用する特定のプロバイダーを選択することもできます。",
"learnMore": "プロバイダールーティングについて詳しく知る"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter 제공자 라우팅",
"description": "OpenRouter는 귀하의 모델에 가장 적합한 사용 가능한 제공자에게 요청을 전달합니다. 기본적으로 요청은 가동 시간을 최대화하기 위해 상위 제공자 간에 부하 분산됩니다. 그러나 이 모델에 사용할 특정 제공자를 선택할 수 있습니다.",
"learnMore": "제공자 라우팅에 대해 자세히 알아보기"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter-providerroutering",
"description": "OpenRouter stuurt verzoeken naar de best beschikbare providers voor je model. Standaard worden verzoeken gebalanceerd over de beste providers voor maximale uptime. Je kunt echter een specifieke provider kiezen voor dit model.",
"learnMore": "Meer informatie over providerroutering"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Routing dostawców OpenRouter",
"description": "OpenRouter kieruje żądania do najlepszych dostępnych dostawców dla Twojego modelu. Domyślnie żądania są równoważone między najlepszymi dostawcami, aby zmaksymalizować czas działania. Możesz jednak wybrać konkretnego dostawcę do użycia z tym modelem.",
"learnMore": "Dowiedz się więcej o routingu dostawców"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Roteamento de Provedores OpenRouter",
"description": "OpenRouter direciona solicitações para os melhores provedores disponíveis para seu modelo. Por padrão, as solicitações são balanceadas entre os principais provedores para maximizar o tempo de atividade. No entanto, você pode escolher um provedor específico para usar com este modelo.",
"learnMore": "Saiba mais sobre roteamento de provedores"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Маршрутизация провайдера OpenRouter",
"description": "OpenRouter направляет запросы к лучшим доступным провайдерам для вашей модели. По умолчанию запросы балансируются между топовыми провайдерами для максимальной доступности. Однако вы можете выбрать конкретного провайдера для этой модели.",
"learnMore": "Подробнее о маршрутизации провайдеров"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter Sağlayıcı Yönlendirmesi",
"description": "OpenRouter, modeliniz için mevcut en iyi sağlayıcılara istekleri yönlendirir. Varsayılan olarak, istekler çalışma süresini en üst düzeye çıkarmak için en iyi sağlayıcılar arasında dengelenir. Ancak, bu model için kullanılacak belirli bir sağlayıcı seçebilirsiniz.",
"learnMore": "Sağlayıcı yönlendirmesi hakkında daha fazla bilgi edinin"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "Định tuyến nhà cung cấp OpenRouter",
"description": "OpenRouter chuyển hướng yêu cầu đến các nhà cung cấp tốt nhất hiện có cho mô hình của bạn. Theo mặc định, các yêu cầu được cân bằng giữa các nhà cung cấp hàng đầu để tối đa hóa thời gian hoạt động. Tuy nhiên, bạn có thể chọn một nhà cung cấp cụ thể để sử dụng cho mô hình này.",
"learnMore": "Tìm hiểu thêm về định tuyến nhà cung cấp"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -477,6 +477,10 @@
"title": "OpenRouter 提供商路由",
"description": "OpenRouter 将请求路由到适合您模型的最佳可用提供商。默认情况下,请求会在顶级提供商之间进行负载均衡以最大化正常运行时间。但是,您可以为此模型选择特定的提供商。",
"learnMore": "了解更多"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {

View file

@ -487,6 +487,10 @@
"title": "OpenRouter 供應商路由",
"description": "OpenRouter 會將請求路由到適合您模型的最佳可用供應商。預設情況下,請求會在頂尖供應商之間進行負載平衡以最大化正常運作時間。您也可以為此模型選擇特定的供應商。",
"learnMore": "了解更多關於供應商路由的資訊"
},
"excludeLowQuantization": {
"label": "Exclude low-bit quantization (FP4/FP6/Int4)",
"description": "Only allow higher precision providers (FP8/FP16/BF16/Int8). Helps prevent broken CJK (Korean/Chinese/Japanese) encoding from aggressively quantized models."
}
},
"customModel": {