feat(dashboard): routing/settings UX clarity — share %, Cloud Sync rename, base-URL override (#6147) (#6253)

feat(dashboard): routing/settings UX clarity (#6147) — effective share %, Cloud Sync→Remote Settings Sync rename, opt-in advanced base-URL override. TDD guard routing-settings-ux-6147.test.ts (6/6). Base-reds only. Integrated into release/v3.8.45.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-05 07:46:18 -03:00
committed by GitHub
parent 776a7a3a58
commit cefbcfb278
7 changed files with 154 additions and 12 deletions

View File

@@ -7,6 +7,7 @@
- **feat(providers):** add **Yuanbao (web)** as a cookie-session provider ([#6196](https://github.com/diegosouzapw/OmniRoute/issues/6196)) — `yuanbao-web` (Tencent Yuanbao, `yuanbao.tencent.com`) with cookie-only auth (`hy_user`/`hy_token` + public agent id), SSE→OpenAI translation incl. `reasoning_content`, exposing DeepSeek V3/R1 + Hunyuan / Hunyuan-T1. Regression guard: `tests/unit/providers-yuanbao-web.test.ts`. `together-web` was **deferred** (no verifiable web-session endpoint — needs a captured request) and `huggingchat-web` **dropped** (the existing `huggingchat` already is a web-cookie provider). (thanks @chirag127)
- **feat(providers):** route the built-in **agentrouter** through the dynamic Claude-Code wire image ([#6056](https://github.com/diegosouzapw/OmniRoute/issues/6056)) — a small static allow-set (`CC_WIRE_IMAGE_BUILTINS` in `open-sse/services/ccWireImageBuiltins.ts`), consulted by `isClaudeCodeCompatible` / `isClaudeCodeCompatibleProvider` / `applyFingerprint`, makes agentrouter adopt the CC wire-image headers + fingerprint **while guarding the CC baseUrl/auth branches** so it keeps its own registry `baseUrl` and `x-api-key` auth. Regression guard: `tests/unit/agentrouter-cc-wire-image.test.ts` (asserts the wire image is applied AND agentrouter's baseUrl/auth are preserved). Live WAF-acceptance against agentrouter.org is a VPS validation follow-up (Hard Rule #18).
- **feat(providers):** **bulk-add API keys for Cloudflare Workers AI** ([#6174](https://github.com/diegosouzapw/OmniRoute/issues/6174)) — `cloudflare-ai` is removed from the bulk-add exclusion list and the bulk parser gains a 3-field `name|accountId|apiKey` mode; the bulk route now builds a **per-entry** `providerSpecificData` so each key carries its own `accountId` (fixing the previous shared-object reuse), and both the create + key-validation paths receive it. Regression guard: `tests/unit/bulk-api-key-parser-cloudflare.test.ts`. (thanks @muflifadla38)
- **feat(dashboard):** routing/settings UX clarity ([#6147](https://github.com/diegosouzapw/OmniRoute/issues/6147)) — (1) weighted combos show the **effective routing share %** next to each weight when weights don't sum to 100 (`WeightTotalBar.tsx`); (2) the status widget's user-facing **"Cloud Sync" label is renamed** to "Remote Settings Sync" (`CloudSyncStatus.tsx`; internal ids/state untouched); (3) built-in providers gain an **opt-in advanced base-URL override** (`isBaseUrlOverrideEligibleProvider`, hidden behind an "Advanced" toggle, reusing the existing `providerSpecificData.baseUrl` persistence — not globally widened). Regression guard: `tests/unit/routing-settings-ux-6147.test.ts`.
### 🐛 Bug Fixes

View File

@@ -20,6 +20,20 @@ const WEIGHT_COLORS = [
"bg-indigo-500",
];
/**
* #6147 — effective routing share of a weighted target.
*
* The per-target `weight` values do not have to sum to 100; at routing time each
* target is picked with probability `weight / Σweights`. So a raw weight of 30
* with a total of 60 is an *effective* 50% share. This pure helper computes that
* share (0-100) and guards the `total === 0` case so the UI never renders NaN.
*/
export function effectiveSharePercent(weight: number, total: number): number {
if (!weight || weight <= 0) return 0;
if (!total || total <= 0) return 0;
return (weight / total) * 100;
}
export default function WeightTotalBar({ models }: WeightTotalBarProps) {
const total = models.reduce((sum, m) => sum + (m.weight || 0), 0);
const isValid = total === 100;
@@ -49,6 +63,16 @@ export default function WeightTotalBar({ models }: WeightTotalBarProps) {
className={`inline-block w-1.5 h-1.5 rounded-full ${WEIGHT_COLORS[i % WEIGHT_COLORS.length]}`}
/>
{m.weight}%
{/* #6147 — show the *effective* routing share when weights don't sum to 100 */}
{total > 0 && total !== 100 && (
<span
className="text-text-muted/70"
title="Effective routing share (weight ÷ total)"
>
{" → "}
{Math.round(effectiveSharePercent(m.weight, total))}%
</span>
)}
</span>
)
)}

View File

@@ -23,6 +23,7 @@ import { isClaudeExtraUsageBlockEnabled } from "@/lib/providers/claudeExtraUsage
import { resolveDashboardProviderInfo } from "../../../providerPageUtils";
import {
isBaseUrlConfigurableProvider,
isBaseUrlOverrideEligibleProvider,
getProviderBaseUrlDefault,
getProviderBaseUrlHint,
getProviderBaseUrlPlaceholder,
@@ -153,7 +154,19 @@ export default function EditConnectionModal({
const [showAdvanced, setShowAdvanced] = useState(false);
const showEmail = useEmailPrivacyStore((state) => state.emailsVisible);
const usesBaseUrl = isBaseUrlConfigurableProvider(provider);
// #6147 — built-in providers can opt in to an advanced base-URL override.
// OAuth connections are excluded: their save path does not persist
// providerSpecificData.baseUrl.
const isConfigurableBaseUrl = isBaseUrlConfigurableProvider(provider);
const isBaseUrlOverrideEligible =
connection.authType !== "oauth" && isBaseUrlOverrideEligibleProvider(provider);
const [showBaseUrlOverride, setShowBaseUrlOverride] = useState(
() =>
typeof connection.providerSpecificData?.baseUrl === "string" &&
connection.providerSpecificData.baseUrl.trim().length > 0
);
const usesBaseUrl =
isConfigurableBaseUrl || (isBaseUrlOverrideEligible && showBaseUrlOverride);
const defaultBaseUrl = getProviderBaseUrlDefault(provider);
const isVertex = provider === "vertex" || provider === "vertex-partner";
const isBedrock = provider === "bedrock";
@@ -441,12 +454,18 @@ export default function EditConnectionModal({
let validatedBaseUrl = null;
if (usesBaseUrl) {
const checked = normalizeAndValidateHttpBaseUrl(formData.baseUrl, defaultBaseUrl);
if (checked.error) {
setSaveError(checked.error);
return;
// #6147 — an opt-in override left blank clears it (no default to fall
// back to). Configurable providers keep their existing default-fallback.
if (!isConfigurableBaseUrl && !formData.baseUrl.trim()) {
validatedBaseUrl = null;
} else {
const checked = normalizeAndValidateHttpBaseUrl(formData.baseUrl, defaultBaseUrl);
if (checked.error) {
setSaveError(checked.error);
return;
}
validatedBaseUrl = checked.value;
}
validatedBaseUrl = checked.value;
}
if (!isOAuth && formData.apiKey) {
@@ -937,13 +956,33 @@ export default function EditConnectionModal({
</>
)}
{/* #6147 — opt-in "Advanced → override base URL" for eligible built-ins */}
{!usesBaseUrl && isBaseUrlOverrideEligible && (
<button
type="button"
onClick={() => setShowBaseUrlOverride(true)}
className="self-start text-xs text-primary hover:underline"
>
{providerText(t, "overrideBaseUrlAdvanced", "Advanced: override base URL")}
</button>
)}
{usesBaseUrl && (
<Input
label={t("baseUrlLabel")}
value={formData.baseUrl}
onChange={(e) => setFormData({ ...formData, baseUrl: e.target.value })}
placeholder={getProviderBaseUrlPlaceholder(provider)}
hint={getProviderBaseUrlHint(provider, t)}
hint={
getProviderBaseUrlHint(provider, t) ||
(isBaseUrlOverrideEligible
? providerText(
t,
"overrideBaseUrlHint",
"Advanced: point this built-in provider at a custom endpoint. Leave blank to use the default."
)
: undefined)
}
/>
)}

View File

@@ -257,6 +257,25 @@ export function isBaseUrlConfigurableProvider(providerId?: string | null) {
);
}
/**
* #6147 — whether a built-in provider is eligible for an OPT-IN "Advanced →
* override base URL" affordance in the edit-connection modal.
*
* This does NOT widen the always-on base-URL field: providers already covered by
* `isBaseUrlConfigurableProvider` (the configurable set + self-hosted) keep their
* existing dedicated field and return `false` here. Every *other* provider id is
* eligible to opt in per-connection so an operator can hot-fix a broken built-in
* preset by pointing it at a custom endpoint. The field stays hidden until the
* user explicitly reveals it (or an override was already saved), so nothing is
* exposed by default. OAuth connections are excluded at the call site, since
* their save path does not persist `providerSpecificData.baseUrl`.
*/
export function isBaseUrlOverrideEligibleProvider(providerId?: string | null): boolean {
if (!providerId) return false;
if (isBaseUrlConfigurableProvider(providerId)) return false;
return true;
}
export function getProviderBaseUrlDefault(providerId?: string | null) {
const localProvider = getLocalProviderMetadata(providerId);
if (typeof localProvider?.localDefault === "string" && localProvider.localDefault.trim()) {

View File

@@ -4849,6 +4849,8 @@
"kimiWebDesc": "Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "Dola Web",
"doubaoWebDesc": "ByteDance AI chat via dola.com",
"overrideBaseUrlAdvanced": "Advanced: override base URL",
"overrideBaseUrlHint": "Advanced: point this built-in provider at a custom endpoint. Leave blank to use the default.",
"bulkAddFormatHintCloudflare": "One key per line. Format: name|accountId|apiKey (Cloudflare account ID + API token)."
},
"settings": {

View File

@@ -13,11 +13,15 @@
import { useState, useEffect, useRef, useCallback } from "react";
import { useRouter } from "next/navigation";
// #6147 — user-facing labels renamed from "Cloud …" to "Remote Settings Sync"
// wording (this feature syncs the operator's own settings to their own remote
// store — it is not a cloud/telemetry service). Internal state keys, the
// `cloud_*` material icons and the cloudSync.* wiring are intentionally kept.
const STATUS_CONFIG = {
connected: { icon: "cloud_done", color: "text-green-500", label: "Cloud" },
connected: { icon: "cloud_done", color: "text-green-500", label: "Synced" },
syncing: { icon: "cloud_sync", color: "text-blue-400 animate-pulse", label: "Syncing..." },
disconnected: { icon: "cloud_off", color: "text-amber-500", label: "Cloud Off" },
error: { icon: "cloud_off", color: "text-red-400", label: "Cloud Error" },
disconnected: { icon: "cloud_off", color: "text-amber-500", label: "Sync Off" },
error: { icon: "cloud_off", color: "text-red-400", label: "Sync Error" },
disabled: { icon: "cloud_off", color: "text-text-muted/50", label: "Disabled" },
};
@@ -80,10 +84,10 @@ export default function CloudSyncStatus({ collapsed = false }) {
className="flex items-center gap-2 px-3 py-1.5 text-xs rounded-lg hover:bg-white/5 transition-colors cursor-pointer w-full"
title={
lastSync
? `Cloud ${status === "connected" ? "connected" : "disconnected"} — Last sync: ${lastSync.toLocaleTimeString()}`
? `Remote settings sync ${status === "connected" ? "connected" : "disconnected"} — Last sync: ${lastSync.toLocaleTimeString()}`
: config.label
}
aria-label={`Cloud sync status: ${config.label}`}
aria-label={`Remote settings sync status: ${config.label}`}
>
<span className={`material-symbols-outlined text-[16px] ${config.color}`} aria-hidden="true">
{config.icon}

View File

@@ -0,0 +1,53 @@
// #6147 — routing/settings UX clarity. Pure-function guards for two of the three
// sub-fixes:
// 1. Weighted effective-share % (weight ÷ Σweights), incl. total===0 and sum≠100.
// 3. Opt-in base-URL override eligibility predicate for built-in providers.
// (Item 2 is a display-only label rename with no pure seam — manual verification.)
import { test } from "node:test";
import assert from "node:assert/strict";
import { effectiveSharePercent } from "../../src/app/(dashboard)/dashboard/combos/WeightTotalBar.tsx";
import {
isBaseUrlOverrideEligibleProvider,
isBaseUrlConfigurableProvider,
} from "../../src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts";
test("effectiveSharePercent — sum === 100 keeps raw weight", () => {
assert.equal(effectiveSharePercent(30, 100), 30);
assert.equal(effectiveSharePercent(70, 100), 70);
});
test("effectiveSharePercent — sum !== 100 rescales to the effective share", () => {
// Two equal weights of 30 (total 60) are each an effective 50%.
assert.equal(effectiveSharePercent(30, 60), 50);
// Over-100 total: 150 total, weight 75 → 50%.
assert.equal(effectiveSharePercent(75, 150), 50);
});
test("effectiveSharePercent — guards total === 0 and non-positive weight (no NaN)", () => {
assert.equal(effectiveSharePercent(0, 0), 0);
assert.equal(effectiveSharePercent(50, 0), 0);
assert.equal(effectiveSharePercent(0, 100), 0);
assert.equal(effectiveSharePercent(-10, 100), 0);
assert.ok(!Number.isNaN(effectiveSharePercent(10, 0)));
});
test("isBaseUrlOverrideEligibleProvider — built-in providers are opt-in eligible", () => {
// A plain built-in that has no dedicated base-URL field today.
assert.equal(isBaseUrlOverrideEligibleProvider("openai"), true);
assert.equal(isBaseUrlOverrideEligibleProvider("groq"), true);
});
test("isBaseUrlOverrideEligibleProvider — providers with an always-on field are NOT re-offered", () => {
// Everything already covered by the configurable set stays false here so the
// override does not double up on the existing dedicated field.
for (const id of ["azure-openai", "databricks", "siliconflow"]) {
assert.equal(isBaseUrlConfigurableProvider(id), true);
assert.equal(isBaseUrlOverrideEligibleProvider(id), false);
}
});
test("isBaseUrlOverrideEligibleProvider — empty/nullish ids are not eligible", () => {
assert.equal(isBaseUrlOverrideEligibleProvider(""), false);
assert.equal(isBaseUrlOverrideEligibleProvider(null), false);
assert.equal(isBaseUrlOverrideEligibleProvider(undefined), false);
});