Compare commits

...

2 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza 6acd36e374 Merge pull request #655 from oSoWoSo/dev
Build Electron Desktop App / Validate version (push) Failing after 34s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
Build Electron Desktop App / Publish to npm (push) Has been skipped
Merged! Thanks @zen0bit for polishing the Czech translations 🇨🇿
2026-03-26 23:50:54 -03:00
zenobit af51eecbac i18n: Improve some strings 2026-03-27 03:33:53 +01:00
7 changed files with 58 additions and 25 deletions
+12
View File
@@ -4,6 +4,18 @@
---
## [3.1.4] — 2026-03-27
### 🐛 Bug Fixes
- **Streaming Override Fix** — Explicit `stream: true` in request body now takes priority over `Accept: application/json` header. Clients sending both will correctly receive SSE streaming responses (#656)
### 🌍 i18n
- **Czech string improvements** — Refined terminology across `cs.json` (PR #655 by @zen0bit)
---
## [3.1.3] — 2026-03-26
### 🌍 i18n & Community
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: OmniRoute API
version: 3.1.3
version: 3.1.4
description: |
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
endpoint that routes requests to multiple AI providers with load balancing,
+9 -2
View File
@@ -13,10 +13,17 @@ export function clientWantsJsonResponse(acceptHeader: unknown): boolean {
/**
* Resolves stream behavior from request body + Accept header.
* OpenAI-compatible behavior: stream only when `stream: true` and client did not force JSON.
* Priority: explicit `stream: true/false` in body wins.
* Accept header only acts as fallback when stream is not explicitly set.
* Fixes #656: clients sending both `stream: true` and `Accept: application/json`
* should still get streaming responses — body intent takes precedence.
*/
export function resolveStreamFlag(bodyStream: unknown, acceptHeader: unknown): boolean {
return bodyStream === true && !clientWantsJsonResponse(acceptHeader);
// Explicit body value always wins
if (bodyStream === true) return true;
if (bodyStream === false) return false;
// No explicit stream param — fall back to Accept header heuristic
return !clientWantsJsonResponse(acceptHeader);
}
/**
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "3.1.3",
"version": "3.1.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "3.1.3",
"version": "3.1.4",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "3.1.3",
"version": "3.1.4",
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
"type": "module",
"bin": {
+17 -17
View File
@@ -42,7 +42,7 @@
"reloadPage": "Znovu načíst stránku",
"connected": "Připojeno",
"disconnected": "Odpojeno",
"notConfigured": "Nenakonfigurováno",
"notConfigured": "Nenastaveno",
"testConnection": "Test připojení",
"enable": "Umožnit",
"disable": "Zakázat",
@@ -241,7 +241,7 @@
"providersOverview": "Přehled poskytovatelů",
"configuredOf": "{configured} nastaveno z {total} dostupných poskytovatelů",
"noModelsAvailable": "Pro tohoto poskytovatele nejsou k dispozici žádné modely.",
"configureFirst": "Nejprve nakonfigurujte připojení v {providers}",
"configureFirst": "Nejprve nastavte připojení v {providers}",
"configureProvider": "Nastavení poskytovatele",
"modelAvailable": "Dostupný model: {count}",
"modelsAvailable": "Dostupné modely: {count}",
@@ -407,7 +407,7 @@
"generate": "Generovat",
"generating": "Generuji...",
"loadingModels": "Načítání dostupných modelů...",
"noModels": "Žádné modely k dispozici. Nejprve nakonfigurujte poskytovatele s mediálními funkcemi.",
"noModels": "Žádné modely k dispozici. Nejprve nastavte poskytovatele s mediálními funkcemi.",
"error": "Generování selhalo",
"result": "Výsledky",
"imageDescription": "Generujte obrázky z textových pokynů pomocí OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI a dalších.",
@@ -425,7 +425,7 @@
"rerankResults": "Rerank výsledky",
"searchHistory": "Historie vyhledávání",
"urlOverlap": "Překrývání URL adres",
"noSearchProviders": "Nejsou nakonfigurováni žádní poskytovatelé vyhledávání. Přidejte poskytovatele v Nastavení.",
"noSearchProviders": "Nejsou nastaveni žádní poskytovatelé vyhledávání. Přidejte poskytovatele v Nastavení.",
"noRerankModels": "Není k dispozici žádný rerank model",
"webSearch": "Vyhledávání na webu",
"provider": "Poskytovatel",
@@ -478,7 +478,7 @@
"baseUrl": "Základní URL",
"apiKey": "API klíč",
"configured": "Nastaveno",
"notConfigured": "Nekonfigurováno",
"notConfigured": "Nenastaveno",
"notInstalled": "Neinstalováno",
"custom": "Vlastní",
"unknown": "Neznámý",
@@ -621,7 +621,7 @@
"codex": "Použijte, pokud váš tým používá OpenAI Codex CLI a autentizaci založenou na profilech.",
"droid": "Použijte, pokud potřebujete odlehčeného agenta terminálu zaměřeného na rychlé kódování a smyčky provádění příkazů.",
"openclaw": "Použijte, pokud chcete kódovacího agenta ve stylu Open Claw, ale směrovaného přes zásady OmniRoute.",
"cline": "Použijte, pokud konfigurujete kódovací agenty uvnitř editorů a chcete návodné nastavení s modely OmniRoute.",
"cline": "Použijte, pokud nastavujete kódovací agenty uvnitř editorů a chcete návodné nastavení s modely OmniRoute.",
"kilo": "Použijte, pokud váš pracovní postup závisí na příkazech Kilo Code a rychlých iterativních úpravách.",
"cursor": "Použijte při kódování v Cursoru, pokud potřebujete vlastní modely kompatibilní s OpenAI přes OmniRoute.",
"continue": "Použijte, pokud spouštíte Continue v IDE a potřebujete přenosnou nastavení poskytovatele založenou na JSON.",
@@ -712,7 +712,7 @@
}
},
"notes": {
"0": "OpenCode vyžaduje konfiguraci API klíče.",
"0": "OpenCode vyžaduje nastavení API klíče.",
"1": "Nastavte základní URL na váš OmniRoute endpoint."
}
},
@@ -777,7 +777,7 @@
"more": "+{count} dalších",
"reqs": "požadavky",
"success": "úspěch",
"proxyConfigured": "Proxy nakonfigurována",
"proxyConfigured": "Proxy nastavena",
"copyComboName": "Kopírovat název komba",
"enableCombo": "Povolit kombo",
"disableCombo": "Zakázat kombo",
@@ -1051,7 +1051,7 @@
"openA2aDashboard": "Otevřít správu A2A",
"mcpQuickStartTitle": "Rychlý start MCP",
"mcpQuickStartStep1": "Spusťte MCP server pomocí příkazu `omniroute --mcp`.",
"mcpQuickStartStep2": "Nakonfigurujte klienta MCP pro připojení přes transport stdio.",
"mcpQuickStartStep2": "Nastavte klienta MCP pro připojení přes transport stdio.",
"mcpQuickStartStep3": "Spusťte nástroje jako `omniroute_get_health` a `omniroute_list_combos`.",
"a2aQuickStartTitle": "Rychlý start A2A",
"a2aQuickStartStep1": "Kartu agenta najdete na adrese `/.well-known/agent.json`.",
@@ -1395,7 +1395,7 @@
"addAnthropicCompatible": "Přidat Anthropic Kompatibilní",
"addNewProvider": "Přidat nového poskytovatele",
"backToProviders": "Zpět k poskytovatelům",
"configureNewProvider": "Nakonfigurujte nového AI poskytovatele pro použití s vašimi aplikacemi.",
"configureNewProvider": "Nastavte nového AI poskytovatele pro použití s vašimi aplikacemi.",
"providerLabel": "Poskytovatel",
"selectProvider": "Vyberte poskytovatele",
"selectedProvider": "Vybraný poskytovatel",
@@ -1634,7 +1634,7 @@
"modelsPathPlaceholder": "/models",
"modelsPathHint": "Cesta k vlastním modelům pro validaci (např. /v4/models)",
"autoSync": "Automatická synchronizace",
"autoSyncTooltip": "Automaticky obnovuje seznam modelů každých 24 hodin (lze konfigurovat přes MODEL_SYNC_INTERVAL_HOURS)",
"autoSyncTooltip": "Automaticky obnovuje seznam modelů každých 24 hodin (lze nastavit přes MODEL_SYNC_INTERVAL_HOURS)",
"autoSyncEnabled": "Automatická synchronizace povolena modely se budou pravidelně obnovovat",
"autoSyncDisabled": "Automatická synchronizace zakázána",
"autoSyncToggleFailed": "Nepodařilo se přepnout automatickou synchronizaci",
@@ -1737,7 +1737,7 @@
"loadingCacheStats": "Načítám statistiky mezipaměti…",
"globalProxy": "Globální Proxy",
"globalProxyDesc": "Nastavte globální odchozí proxy pro všechna API volání. Jednotliví poskytovatelé, komba a klíče mohou toto přepsat.",
"noGlobalProxy": "Není nakonfigurována globální proxy",
"noGlobalProxy": "Není nastavena globální proxy",
"globalLabel": "Globální",
"configure": "Nastavit",
"globalSystemPrompt": "Globální systémový Prompt",
@@ -2429,7 +2429,7 @@
"loading": "Načítám...",
"invalidPassword": "Neplatné heslo",
"errorOccurredRetry": "Došlo k chybě. Zkuste to prosím znovu.",
"configureInstance": "Pojďme nakonfigurovat vaši instanci OmniRoute",
"configureInstance": "Pojďme nastavit vaši instanci OmniRoute",
"runOnboardingWizard": "Spusťte průvodce zavedením, nastavte si heslo a připojte svého prvního AI poskytovatele.",
"startOnboarding": "Začít s nástupem",
"secureYourInstance": "Zabezpečte svou instanci",
@@ -2516,7 +2516,7 @@
"howItWorksStep3Title": "3. AI Poskytovatelé",
"howItWorksStep3Description": "Požadavek je okamžitě splněn OpenAI, Anthropic, Gemini nebo jinými.",
"getStartedIn30Seconds": "Začněte za 30 sekund",
"getStartedDescription": "Nainstalujte si OmniRoute, nakonfigurujte poskytovatele přes webovou nástěnku a začněte směrovat AI požadavky.",
"getStartedDescription": "Nainstalujte si OmniRoute, nastavte poskytovatele přes webovou nástěnku a začněte směrovat AI požadavky.",
"installOmniRoute": "Instalace OmniRoute",
"installStepDescription": "Spuštěním příkazu npx okamžitě spustíte server",
"openDashboard": "Otevřít nástěnku",
@@ -2530,7 +2530,7 @@
"serverRunningOnLabel": "Server běží na",
"dashboardLabel": "Nástěnka",
"readyToRoute": "Připraveno k trasování! ✓",
"configureProvidersNote": "📝 Nakonfigurujte poskytovatele na nástěnce nebo použijte proměnné prostředí",
"configureProvidersNote": "📝 Nastavte poskytovatele na nástěnce nebo použijte proměnné prostředí",
"dataLocation": "Umístění dat:",
"dataLocationMacLinux": "macOS/Linux:",
"dataLocationWindows": "Windows:",
@@ -2608,7 +2608,7 @@
"featureHealthTitle": "Monitorování zdraví",
"featureHealthText": "Kontroly stavu v reálném čase, stav poskytovatele, stavy jističů a automatická detekce limitů sazeb s exponenciálním odpočítáváním.",
"featureCliTitle": "CLI Nástroje",
"featureCliText": "Spravujte nastavení IDE, exportujte/importujte zálohy, vyhledávejte profily Codex a konfigurujte nastavení z nástěnky.",
"featureCliText": "Spravujte nastavení IDE, exportujte/importujte zálohy, vyhledávejte profily Codex a upravte nastavení z nástěnky.",
"featureSecurityTitle": "Zabezpečení a zásady",
"featureSecurityText": "Ověřování klíčů API, filtrování IP adres, ochrana proti vkládání výzev, zásady domény, správa relací a protokolování auditu.",
"featureCloudSyncTitle": "Synchronizace s cloudem",
@@ -2735,7 +2735,7 @@
"termsResponsibilityCompliance": "Musíte dodržovat podmínky služby každého AI poskytovatele, k jehož API přistupujete prostřednictvím OmniRoute.",
"termsResponsibilitySecurity": "Nesete odpovědnost za zabezpečení vaší lokální instalace OmniRoute, včetně nastavení hesla a omezení přístupu k síti.",
"termsSection3Title": "3. Jak to funguje",
"termsSection3Text": "OmniRoute funguje jako zprostředkující proxy. API Volání odeslaná do OmniRoute jsou překládána a přeposílána vámi nakonfigurovaným AI poskytovatelům. OmniRoute nemění obsah vašich požadavků ani odpovědí nad rámec nezbytného překladu protokolu.",
"termsSection3Text": "OmniRoute funguje jako zprostředkující proxy. API Volání odeslaná do OmniRoute jsou překládána a přeposílána vámi nastaveným AI poskytovatelům. OmniRoute nemění obsah vašich požadavků ani odpovědí nad rámec nezbytného překladu protokolu.",
"termsSection4Title": "4. Zpracování dat",
"termsDataStoredLocally": "Všechna data jsou uložena lokálně na vašem počítači v databázi SQLite.",
"termsNoTransmission": "OmniRoute nepřenáší žádná data na externí servery, pokud explicitně nepovolíte funkce synchronizace s cloudem.",
@@ -4,9 +4,10 @@ import assert from "node:assert/strict";
const { clientWantsJsonResponse, resolveStreamFlag, stripMarkdownCodeFence } =
await import("../../open-sse/utils/aiSdkCompat.ts");
test("T26: Accept application/json disables SSE stream mode", () => {
test("T26: explicit stream:true takes priority over Accept application/json (#656)", () => {
assert.equal(clientWantsJsonResponse("application/json"), true);
assert.equal(resolveStreamFlag(true, "application/json"), false);
// Body stream:true always wins — even with Accept: application/json
assert.equal(resolveStreamFlag(true, "application/json"), true);
});
test("T26: text/event-stream keeps SSE behavior", () => {
@@ -28,3 +29,16 @@ test("T26: non-fenced content is returned unchanged", () => {
const plain = '{"name":"omniroute"}';
assert.equal(stripMarkdownCodeFence(plain), plain);
});
test("T26: undefined stream falls back to Accept header heuristic (#656)", () => {
// No explicit stream param — Accept: application/json means no streaming
assert.equal(resolveStreamFlag(undefined, "application/json"), false);
// No explicit stream param + no JSON accept = stream by default
assert.equal(resolveStreamFlag(undefined, "text/event-stream"), true);
assert.equal(resolveStreamFlag(undefined, undefined), true);
});
test("T26: explicit stream:false always prevents streaming", () => {
assert.equal(resolveStreamFlag(false, "text/event-stream"), false);
assert.equal(resolveStreamFlag(false, undefined), false);
});