docs: update changelog for v1.4.11 release

This commit is contained in:
diegosouzapw
2026-02-25 19:25:07 -03:00
parent 8abdf68718
commit fbaf30a6bf
3 changed files with 61 additions and 52 deletions
+16
View File
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
---
## [1.4.11] — 2026-02-25
> ### 🐛 Settings Persistence Fix
>
> Fixes routing strategy and wildcard aliases not saving after page refresh.
### 🐛 Bug Fixes
- **Routing Strategy Not Saved After Refresh (#134)** — Added `fallbackStrategy`, `wildcardAliases`, and `stickyRoundRobinLimit` to the Zod validation schema. These fields were silently stripped during validation, preventing them from being persisted to the database
### 📝 Notes
- **#135 Closed** — Feature request for proxy configuration (global + per-provider) was already implemented in v1.4.10
---
## [1.4.10] — 2026-02-25
> ### 🔒 Proxy Visibility + Bug Fixes
@@ -99,8 +99,8 @@ export default function ChatTesterMode() {
steps.push({
id: 1,
name: "Client Request",
description: "The request body as your client would send it",
name: t("clientRequest"),
description: t("clientRequestDescription"),
format: clientFormat,
content: JSON.stringify(clientRequest, null, 2),
status: "done",
@@ -117,8 +117,8 @@ export default function ChatTesterMode() {
steps.push({
id: 2,
name: "Format Detected",
description: "OmniRoute auto-detects the API format from the request structure",
name: t("formatDetected"),
description: t("formatDetectedDescription"),
format: detectedFormat,
content: JSON.stringify(
{ detectedFormat, clientFormat, match: detectedFormat === clientFormat },
@@ -143,8 +143,8 @@ export default function ChatTesterMode() {
steps.push({
id: 3,
name: "OpenAI Intermediate",
description: "All formats are first normalized to OpenAI format (the universal bridge)",
name: t("openaiIntermediate"),
description: t("openaiIntermediateDescription"),
format: "openai",
content: JSON.stringify(toOpenaiData.result || toOpenaiData, null, 2),
status: toOpenaiData.success ? "done" : "error",
@@ -166,8 +166,8 @@ export default function ChatTesterMode() {
steps.push({
id: 4,
name: "Provider Format",
description: `OpenAI format is translated to the provider's native format`,
name: t("providerFormat"),
description: t("providerFormatDescription"),
format: targetFmt,
content: JSON.stringify(providerTargetData.result || providerTargetData, null, 2),
status: providerTargetData.success ? "done" : "error",
@@ -181,18 +181,21 @@ export default function ChatTesterMode() {
});
if (!sendRes.ok) {
const errData = await sendRes.json().catch(() => ({ error: "Request failed" }));
const errData = await sendRes.json().catch(() => ({ error: t("requestFailed") }));
steps.push({
id: 5,
name: "Provider Response",
description: "The raw response from the provider API",
name: t("providerResponse"),
description: t("providerResponseRawDescription"),
format: targetFmt,
content: JSON.stringify(errData, null, 2),
status: "error",
});
setChatHistory((prev) => [
...prev,
{ role: "assistant", content: `Error: ${errData.error || "Request failed"}` },
{
role: "assistant",
content: t("errorMessage", { message: errData.error || t("requestFailed") }),
},
]);
} else {
// Read streaming response
@@ -208,8 +211,8 @@ export default function ChatTesterMode() {
steps.push({
id: 5,
name: "Provider Response",
description: "The raw SSE stream from the provider API",
name: t("providerResponse"),
description: t("providerResponseSseDescription"),
format: targetFmt,
content:
fullResponse.slice(0, 5000) + (fullResponse.length > 5000 ? "\n... (truncated)" : ""),
@@ -220,19 +223,22 @@ export default function ChatTesterMode() {
const assistantText = extractAssistantText(fullResponse);
setChatHistory((prev) => [
...prev,
{ role: "assistant", content: assistantText || "(No text extracted)" },
{ role: "assistant", content: assistantText || t("noTextExtracted") },
]);
}
} catch (err) {
steps.push({
id: steps.length + 1,
name: "Error",
description: "An unexpected error occurred",
name: t("error"),
description: t("unexpectedError"),
format: "error",
content: JSON.stringify({ error: err.message }, null, 2),
status: "error",
});
setChatHistory((prev) => [...prev, { role: "assistant", content: `Error: ${err.message}` }]);
setChatHistory((prev) => [
...prev,
{ role: "assistant", content: t("errorMessage", { message: err.message }) },
]);
}
setPipeline(steps);
@@ -250,13 +256,10 @@ export default function ChatTesterMode() {
</span>
<div>
<p className="font-medium text-text-main mb-0.5">{t("pipelineDebugger")}</p>
<p>{t("chatTesterDescription")}</p>
<p>
Send messages as a specific client format and see how each step of the translation
pipeline works. The right panel shows the full flow:{" "}
<strong className="text-text-main">
Client Request Format Detection OpenAI Intermediate Provider Format Response
</strong>
. Click any step to inspect the data at that stage.
<strong className="text-text-main">{t("chatTesterFlow")}</strong>.{" "}
{t("clickStepToInspect")}
</p>
</div>
</div>
@@ -270,7 +273,7 @@ export default function ChatTesterMode() {
<div className="flex flex-col sm:flex-row gap-3">
<div className="flex-1">
<label className="block text-xs font-medium text-text-muted mb-1 uppercase tracking-wider">
Client Format
{t("clientFormat")}
</label>
<Select
value={clientFormat}
@@ -282,7 +285,7 @@ export default function ChatTesterMode() {
</div>
<div className="flex-1">
<label className="block text-xs font-medium text-text-muted mb-1 uppercase tracking-wider">
Provider
{t("provider")}
</label>
<Select
value={provider}
@@ -293,7 +296,7 @@ export default function ChatTesterMode() {
</div>
<div>
<label className="block text-xs font-medium text-text-muted mb-1 uppercase tracking-wider">
Model
{t("model")}
</label>
<div className="relative">
<input
@@ -301,7 +304,7 @@ export default function ChatTesterMode() {
value={model}
onChange={(e) => setModel(e.target.value)}
list="model-suggestions"
placeholder="Select or type a model name..."
placeholder={t("modelPlaceholder")}
className="w-full bg-bg-subtle border border-border rounded-lg px-3 py-2 text-sm text-text-main placeholder:text-text-muted focus:outline-none focus:border-primary transition-colors"
/>
<datalist id="model-suggestions">
@@ -322,13 +325,10 @@ export default function ChatTesterMode() {
<span className="material-symbols-outlined text-[48px] mb-3 opacity-30">
chat
</span>
<p className="text-sm font-medium mb-1">
Send a message to see the translation pipeline
</p>
<p className="text-sm font-medium mb-1">{t("sendMessageToSeePipeline")}</p>
<p className="text-xs text-center max-w-xs">
Your message will be formatted as a{" "}
<strong>{FORMAT_META[clientFormat]?.label}</strong> request, translated through
the pipeline, and sent to the selected provider.
{t("chatMessageHintPrefix")} <strong>{FORMAT_META[clientFormat]?.label}</strong>{" "}
{t("chatMessageHintSuffix")}
</p>
</div>
)}
@@ -346,8 +346,8 @@ export default function ChatTesterMode() {
>
<p className="text-[10px] font-semibold text-text-muted mb-1 uppercase">
{msg.role === "user"
? `You (${FORMAT_META[clientFormat]?.label})`
: "Assistant"}
? t("youWithFormat", { format: FORMAT_META[clientFormat]?.label })
: t("assistant")}
</p>
<p className="whitespace-pre-wrap">{msg.content}</p>
</div>
@@ -364,7 +364,7 @@ export default function ChatTesterMode() {
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && handleSend()}
placeholder="Type a message..."
placeholder={t("typeMessage")}
className="flex-1 bg-bg-subtle border border-border rounded-lg px-3 py-2 text-sm text-text-main placeholder:text-text-muted focus:outline-none focus:border-primary transition-colors"
disabled={sending}
/>
@@ -374,7 +374,7 @@ export default function ChatTesterMode() {
loading={sending}
disabled={!message.trim() || sending}
>
Send
{t("send")}
</Button>
</div>
</div>
@@ -391,9 +391,7 @@ export default function ChatTesterMode() {
</span>
<h3 className="text-sm font-semibold text-text-main">{t("translationPipeline")}</h3>
</div>
<p className="text-xs text-text-muted">
Click on any step to inspect the data at that stage
</p>
<p className="text-xs text-text-muted">{t("clickStepToInspect")}</p>
</div>
</Card>
@@ -403,11 +401,8 @@ export default function ChatTesterMode() {
<span className="material-symbols-outlined text-[48px] mb-3 opacity-30">
account_tree
</span>
<p className="text-sm font-medium mb-1">Pipeline visualization</p>
<p className="text-xs text-center max-w-xs">
Send a message to see how your request flows through detection translation
provider call.
</p>
<p className="text-sm font-medium mb-1">{t("pipelineVisualization")}</p>
<p className="text-xs text-center max-w-xs">{t("pipelineVisualizationHint")}</p>
</div>
</Card>
) : (
@@ -61,10 +61,10 @@ export default function LiveMonitorMode() {
<div>
<p className="font-medium text-text-main mb-0.5">{t("realtime")}</p>
<p>
{t("liveMonitorDescription")}{" "}
{t("liveMonitorDescriptionPrefix")}{" "}
<strong className="text-text-main">{t("chatTester")}</strong>,{" "}
<strong className="text-text-main">{t("testBench")}</strong>, {t("or")}{" "}
{t("externalApiCalls").toLowerCase()} {t("toGenerateEvents")}
<strong className="text-text-main">{t("testBench")}</strong>
{t("liveMonitorDescriptionSuffix")}
</p>
</div>
</div>
@@ -124,9 +124,7 @@ export default function LiveMonitorMode() {
monitoring
</span>
<p className="text-sm font-medium mb-1">{t("noTranslations")}</p>
<p className="text-xs text-center max-w-sm">
{t("eventsAppearHint")}
</p>
<p className="text-xs text-center max-w-sm">{t("eventsAppearHint")}</p>
<div className="flex flex-wrap gap-2 mt-3 text-xs">
<span className="px-2 py-1 rounded-md bg-bg-subtle border border-border">
{t("chatTesterTab")}