mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 22:52:19 +03:00
Merge PR #2019 and resolve conflicts
This commit is contained in:
@@ -11,6 +11,7 @@ interface ToggleProps {
|
||||
size?: "sm" | "md" | "lg";
|
||||
className?: string;
|
||||
title?: string;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export default function Toggle({
|
||||
@@ -21,6 +22,8 @@ export default function Toggle({
|
||||
disabled = false,
|
||||
size = "md",
|
||||
className,
|
||||
title,
|
||||
ariaLabel,
|
||||
}: ToggleProps) {
|
||||
const sizes = {
|
||||
sm: {
|
||||
@@ -58,7 +61,8 @@ export default function Toggle({
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
aria-label={!label ? description || "Toggle" : undefined}
|
||||
aria-label={ariaLabel || label || description || title || "Toggle"}
|
||||
title={title}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
ProviderCostDonut,
|
||||
ModelOverTimeChart,
|
||||
ProviderTable,
|
||||
ServiceTierBreakdown,
|
||||
ApiKeyFilterDropdown,
|
||||
CustomRangePicker,
|
||||
} from "./analytics";
|
||||
@@ -310,10 +311,10 @@ export default function UsageAnalytics() {
|
||||
color: "text-violet-500",
|
||||
},
|
||||
{
|
||||
icon: "swap_horiz",
|
||||
label: "Fallback Rate",
|
||||
value: `${Number(s.fallbackRatePct || 0).toFixed(1)}%`,
|
||||
color: "text-amber-500",
|
||||
icon: "bolt",
|
||||
label: "Fast Requests",
|
||||
value: fmt(s.fastRequests || 0),
|
||||
color: "text-sky-500",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -330,6 +331,12 @@ export default function UsageAnalytics() {
|
||||
value: `${providerDiversity.toFixed(1)}%`,
|
||||
color: "text-sky-500",
|
||||
},
|
||||
{
|
||||
icon: "swap_horiz",
|
||||
label: "Fallback Rate",
|
||||
value: `${Number(s.fallbackRatePct || 0).toFixed(1)}%`,
|
||||
color: "text-amber-500",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
@@ -350,6 +357,9 @@ export default function UsageAnalytics() {
|
||||
<ProviderCostDonut byProvider={analytics?.byProvider} />
|
||||
</div>
|
||||
|
||||
{/* Fast / Standard service tier split */}
|
||||
<ServiceTierBreakdown byServiceTier={analytics?.byServiceTier} summary={s} />
|
||||
|
||||
{/* Model Usage Over Time (stacked area) */}
|
||||
<ModelOverTimeChart
|
||||
dailyByModel={analytics?.dailyByModel}
|
||||
|
||||
@@ -1081,6 +1081,71 @@ export function ModelTable({ byModel, summary }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function ServiceTierBreakdown({ byServiceTier, summary }) {
|
||||
const data = useMemo(() => byServiceTier || [], [byServiceTier]);
|
||||
const totalRequests = Number(summary?.totalRequests || 0);
|
||||
const totalCost = Number(summary?.totalCost || 0);
|
||||
|
||||
if (!data.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
<div className="p-4 border-b border-border flex items-center justify-between gap-3">
|
||||
<h3 className="text-sm font-semibold text-text-muted uppercase tracking-wider">
|
||||
Service Tier
|
||||
</h3>
|
||||
<span className="text-[11px] text-text-muted">Fast / Standard cost split</span>
|
||||
</div>
|
||||
<div className="divide-y divide-border">
|
||||
{data.map((tier) => {
|
||||
const isFast = tier.serviceTier === "priority";
|
||||
const requestPct =
|
||||
totalRequests > 0
|
||||
? ((Number(tier.requests || 0) / totalRequests) * 100).toFixed(1)
|
||||
: "0";
|
||||
const costPct =
|
||||
totalCost > 0 ? ((Number(tier.cost || 0) / totalCost) * 100).toFixed(1) : "0";
|
||||
return (
|
||||
<div key={tier.serviceTier} className="p-4 flex flex-col gap-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`material-symbols-outlined text-[18px] ${
|
||||
isFast ? "text-sky-500" : "text-text-muted"
|
||||
}`}
|
||||
>
|
||||
{isFast ? "bolt" : "speed"}
|
||||
</span>
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-text-main">{tier.label}</div>
|
||||
<div className="text-xs text-text-muted">
|
||||
{fmtFull(tier.requests)} requests · {fmt(tier.totalTokens)} tokens
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="font-mono text-sm font-semibold text-amber-500">
|
||||
{fmtCost(tier.cost)}
|
||||
</div>
|
||||
<div className="text-xs text-text-muted">{costPct}% of cost</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-1.5 rounded-full bg-black/5 dark:bg-white/10 overflow-hidden">
|
||||
<div
|
||||
className={`h-full rounded-full ${isFast ? "bg-sky-500" : "bg-text-muted/50"}`}
|
||||
style={{ width: `${requestPct}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// ── UsageDetail ────────────────────────────────────────────────────────────
|
||||
|
||||
export function UsageDetail({ summary }) {
|
||||
|
||||
@@ -25,6 +25,7 @@ export {
|
||||
ProviderCostDonut,
|
||||
ModelOverTimeChart,
|
||||
ProviderTable,
|
||||
ServiceTierBreakdown,
|
||||
} from "./charts";
|
||||
|
||||
export { default as ApiKeyFilterDropdown } from "./ApiKeyFilterDropdown";
|
||||
|
||||
@@ -1849,6 +1849,7 @@ export const USAGE_SUPPORTED_PROVIDERS = [
|
||||
"claude",
|
||||
"kimi-coding",
|
||||
"glm",
|
||||
"zai",
|
||||
"glmt",
|
||||
"minimax",
|
||||
"minimax-cn",
|
||||
|
||||
@@ -8,6 +8,7 @@ export const ROUTING_STRATEGY_VALUES = [
|
||||
"random",
|
||||
"least-used",
|
||||
"cost-optimized",
|
||||
"reset-aware",
|
||||
"strict-random",
|
||||
"auto",
|
||||
"lkgp",
|
||||
@@ -123,6 +124,13 @@ export const ROUTING_STRATEGIES: RoutingStrategyOption[] = [
|
||||
settingsDescKey: "costOptDesc",
|
||||
icon: "savings",
|
||||
},
|
||||
{
|
||||
value: "reset-aware",
|
||||
labelKey: "resetAware",
|
||||
combosDescKey: "resetAwareDesc",
|
||||
settingsDescKey: "resetAwareDesc",
|
||||
icon: "event_repeat",
|
||||
},
|
||||
{
|
||||
value: "strict-random",
|
||||
labelKey: "strictRandom",
|
||||
|
||||
@@ -8,6 +8,7 @@ type ReadTimeoutOptions = {
|
||||
|
||||
export const DEFAULT_FETCH_TIMEOUT_MS = 600_000;
|
||||
export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 600_000;
|
||||
export const DEFAULT_STREAM_READINESS_TIMEOUT_MS = 30_000;
|
||||
export const DEFAULT_FETCH_CONNECT_TIMEOUT_MS = 30_000;
|
||||
export const DEFAULT_FETCH_KEEPALIVE_TIMEOUT_MS = 4_000;
|
||||
export const DEFAULT_API_BRIDGE_PROXY_TIMEOUT_MS = 30_000;
|
||||
@@ -24,6 +25,7 @@ function hasEnvValue(env: EnvSource, name: string): boolean {
|
||||
export type UpstreamTimeoutConfig = {
|
||||
fetchTimeoutMs: number;
|
||||
streamIdleTimeoutMs: number;
|
||||
streamReadinessTimeoutMs: number;
|
||||
fetchHeadersTimeoutMs: number;
|
||||
fetchBodyTimeoutMs: number;
|
||||
fetchConnectTimeoutMs: number;
|
||||
@@ -89,10 +91,20 @@ export function getUpstreamTimeoutConfig(
|
||||
logger,
|
||||
}
|
||||
);
|
||||
const streamReadinessTimeoutMs = readTimeoutMs(
|
||||
env,
|
||||
"STREAM_READINESS_TIMEOUT_MS",
|
||||
DEFAULT_STREAM_READINESS_TIMEOUT_MS,
|
||||
{
|
||||
allowZero: true,
|
||||
logger,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
fetchTimeoutMs,
|
||||
streamIdleTimeoutMs,
|
||||
streamReadinessTimeoutMs,
|
||||
fetchHeadersTimeoutMs: readTimeoutMs(env, "FETCH_HEADERS_TIMEOUT_MS", fetchTimeoutMs, {
|
||||
allowZero: true,
|
||||
logger,
|
||||
|
||||
@@ -375,6 +375,7 @@ const comboRuntimeConfigSchema = z
|
||||
strategy: comboStrategySchema.optional(),
|
||||
maxRetries: z.coerce.number().int().min(0).max(10).optional(),
|
||||
retryDelayMs: z.coerce.number().int().min(0).max(60000).optional(),
|
||||
fallbackDelayMs: z.coerce.number().int().min(0).max(60000).optional(),
|
||||
timeoutMs: z.coerce.number().int().min(1000).optional(),
|
||||
concurrencyPerModel: z.coerce.number().int().min(1).max(20).optional(),
|
||||
queueTimeoutMs: z.coerce.number().int().min(1000).max(120000).optional(),
|
||||
@@ -395,6 +396,10 @@ const comboRuntimeConfigSchema = z
|
||||
explorationRate: z.number().min(0).max(1).optional(),
|
||||
routerStrategy: z.string().optional(),
|
||||
compositeTiers: compositeTiersSchema.optional(),
|
||||
resetAwareSessionWeight: z.coerce.number().min(0).max(100).optional(),
|
||||
resetAwareWeeklyWeight: z.coerce.number().min(0).max(100).optional(),
|
||||
resetAwareTieBandPercent: z.coerce.number().min(0).max(100).optional(),
|
||||
resetAwareExhaustionGuardPercent: z.coerce.number().min(0).max(100).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
@@ -439,6 +444,7 @@ export const updateSettingsSchema = z.object({
|
||||
bruteForceProtection: z.boolean().optional(),
|
||||
hiddenSidebarItems: z.array(z.enum(HIDEABLE_SIDEBAR_ITEM_IDS)).optional(),
|
||||
comboConfigMode: z.enum(COMBO_CONFIG_MODES).optional(),
|
||||
codexServiceTier: z.object({ enabled: z.boolean() }).optional(),
|
||||
// Routing settings (#134)
|
||||
fallbackStrategy: settingsFallbackStrategySchema.optional(),
|
||||
wildcardAliases: z.array(z.object({ pattern: z.string(), target: z.string() })).optional(),
|
||||
|
||||
@@ -37,6 +37,7 @@ export const updateSettingsSchema = z.object({
|
||||
debugMode: z.boolean().optional(),
|
||||
hiddenSidebarItems: z.array(z.enum(HIDEABLE_SIDEBAR_ITEM_IDS)).optional(),
|
||||
comboConfigMode: z.enum(COMBO_CONFIG_MODES).optional(),
|
||||
codexServiceTier: z.object({ enabled: z.boolean() }).optional(),
|
||||
// Routing settings (#134)
|
||||
fallbackStrategy: z.enum(ACCOUNT_FALLBACK_STRATEGY_VALUES).optional(),
|
||||
wildcardAliases: z.array(z.object({ pattern: z.string(), target: z.string() })).optional(),
|
||||
|
||||
Reference in New Issue
Block a user