Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions open-sse/mcp-server/schemas/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,7 @@ export const setBudgetGuardTool: McpToolDefinition<
// --- Tool 11: omniroute_set_routing_strategy ---
export const setRoutingStrategyInput = z.object({
comboId: z.string().describe("Combo ID or name to update"),
strategy: z
.enum(ROUTING_STRATEGY_VALUES)
.describe("Routing strategy to apply"),
strategy: z.enum(ROUTING_STRATEGY_VALUES).describe("Routing strategy to apply"),
autoRoutingStrategy: z
.enum(AUTO_ROUTING_STRATEGY_VALUES)
.optional()
Expand Down
4 changes: 3 additions & 1 deletion open-sse/mcp-server/tools/advancedTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,9 @@ export async function handleSetRoutingStrategy(args: {
name: toString(updatedCombo.name, toString(combo.name, comboId)),
strategy: toString(updatedCombo.strategy, normalizedStrategy),
autoRoutingStrategy:
toString(updatedCombo.strategy, normalizedStrategy) === "auto" ? resolvedAutoStrategy : null,
toString(updatedCombo.strategy, normalizedStrategy) === "auto"
? resolvedAutoStrategy
: null,
},
};

Expand Down
6 changes: 1 addition & 5 deletions open-sse/services/autoCombo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,4 @@ export {
export { getTaskFitness, getTaskTypes } from "./taskFitness";
export { SelfHealingManager, getSelfHealingManager } from "./selfHealing";
export { MODE_PACKS, getModePack, getModePackNames } from "./modePacks";
export {
selectProvider,
type AutoComboConfig,
type SelectionResult,
} from "./engine";
export { selectProvider, type AutoComboConfig, type SelectionResult } from "./engine";
13 changes: 10 additions & 3 deletions open-sse/services/combo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,14 +665,18 @@ function sortTargetsByContextSize(targets: ResolvedComboTarget[]) {
.filter((target): target is ResolvedComboTarget => target !== null);
}

function getP2CTargetScore(target: ResolvedComboTarget, metrics: ReturnType<typeof getComboMetrics>): number {
function getP2CTargetScore(
target: ResolvedComboTarget,
metrics: ReturnType<typeof getComboMetrics>
): number {
const breakerState = getCircuitBreaker(target.provider)?.getStatus?.()?.state;
if (breakerState === "OPEN") return -Infinity;
const modelMetric = metrics?.byModel?.[target.modelStr] || null;
const successRate = Number(modelMetric?.successRate);
const avgLatency = Number(modelMetric?.avgLatencyMs);
const successScore = Number.isFinite(successRate) ? successRate / 100 : 0.5;
const latencyScore = Number.isFinite(avgLatency) && avgLatency > 0 ? 1 / Math.log10(avgLatency + 10) : 0.25;
const latencyScore =
Number.isFinite(avgLatency) && avgLatency > 0 ? 1 / Math.log10(avgLatency + 10) : 0.25;
const breakerPenalty = breakerState === "HALF_OPEN" ? 0.25 : 0;
return successScore + latencyScore - breakerPenalty;
}
Expand Down Expand Up @@ -1465,7 +1469,10 @@ export async function handleComboChat({
orderedTargets = fisherYatesShuffle([...orderedTargets]);
log.info("COMBO", `Random shuffle: ${orderedTargets.length} targets`);
} else if (strategy === "fill-first") {
log.info("COMBO", `Fill-first ordering: preserving priority order (${orderedTargets.length} targets)`);
log.info(
"COMBO",
`Fill-first ordering: preserving priority order (${orderedTargets.length} targets)`
);
} else if (strategy === "p2c") {
orderedTargets = orderTargetsByPowerOfTwoChoices(orderedTargets, combo.name);
log.info("COMBO", `Power-of-two-choices ordering: selected ${orderedTargets[0]?.modelStr}`);
Expand Down
5 changes: 5 additions & 0 deletions src/app/(dashboard)/dashboard/compression/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { redirect } from "next/navigation";

export const metadata = {
title: "Compression",
description: "Configure context compression settings to reduce token usage and costs.",
};

export default function CompressionPage() {
redirect("/dashboard/context/caveman");
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ export default function ComboDefaultsTab() {
setComboDefaults((prev) => ({
...prev,
...sanitizeComboRuntimeConfig(comboData.comboDefaults),
strategy: comboData.comboDefaults?.strategy ?? settingsData.fallbackStrategy ?? prev.strategy,
strategy:
comboData.comboDefaults?.strategy ?? settingsData.fallbackStrategy ?? prev.strategy,
stickyRoundRobinLimit:
settingsData.stickyRoundRobinLimit ??
comboData.comboDefaults?.stickyRoundRobinLimit ??
Expand Down
6 changes: 0 additions & 6 deletions src/app/(dashboard)/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import SystemStorageTab from "./components/SystemStorageTab";
import SecurityTab from "./components/SecurityTab";
import RoutingTab from "./components/RoutingTab";
import ComboDefaultsTab from "./components/ComboDefaultsTab";
import ProxyTab from "./components/ProxyTab";
import AppearanceTab from "./components/AppearanceTab";
import ThinkingBudgetTab from "./components/ThinkingBudgetTab";
import SystemPromptTab from "./components/SystemPromptTab";
Expand All @@ -23,7 +22,6 @@ import ResilienceTab from "./components/ResilienceTab";
import CliproxyapiSettingsTab from "./components/CliproxyapiSettingsTab";
import PayloadRulesTab from "./components/PayloadRulesTab";
import VisionBridgeSettingsTab from "./components/VisionBridgeSettingsTab";
import MitmProxyTab from "./components/MitmProxyTab";
import ModelRoutingSection from "@/shared/components/ModelRoutingSection";

const tabs = [
Expand All @@ -33,7 +31,6 @@ const tabs = [
{ id: "security", labelKey: "security", icon: "shield" },
{ id: "routing", labelKey: "routing", icon: "route" },
{ id: "resilience", labelKey: "resilience", icon: "electrical_services" },
{ id: "mitm", labelKey: "mitmProxy", icon: "lan" },
{ id: "advanced", labelKey: "advanced", icon: "tune" },
];

Expand Down Expand Up @@ -138,12 +135,9 @@ export default function SettingsPage() {

{activeTab === "resilience" && <ResilienceTab />}

{activeTab === "mitm" && <MitmProxyTab />}

{activeTab === "advanced" && (
<div className="flex flex-col gap-6">
<PayloadRulesTab />
<ProxyTab />
<CliproxyapiSettingsTab />
</div>
)}
Expand Down
79 changes: 79 additions & 0 deletions src/app/(dashboard)/dashboard/system/proxy/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"use client";

import { useState } from "react";
import { useTranslations } from "next-intl";
import { cn } from "@/shared/utils/cn";
import ProxyTab from "@/app/(dashboard)/dashboard/settings/components/ProxyTab";
import MitmProxyTab from "@/app/(dashboard)/dashboard/settings/components/MitmProxyTab";
import OneproxyTab from "@/app/(dashboard)/dashboard/settings/components/OneproxyTab";

const subTabs = [
{ id: "http", labelKey: "httpProxy", icon: "dns" },
{ id: "mitm", labelKey: "mitmProxy", icon: "lan" },
{ id: "oneproxy", labelKey: "1proxy", icon: "public" },
];

export default function ProxyPage() {
const t = useTranslations("settings");
const [activeSubTab, setActiveSubTab] = useState("http");

return (
<div className="max-w-6xl mx-auto min-w-0">
<div className="flex flex-col gap-6">
<div className="w-full overflow-x-auto pb-1">
<div
role="tablist"
aria-label={t("proxySubTabsAria")}
className="inline-flex items-center p-1 rounded-lg bg-black/5 dark:bg-white/5 min-w-max"
>
{subTabs.map((subTab) => (
<button
key={subTab.id}
role="tab"
aria-selected={activeSubTab === subTab.id}
tabIndex={activeSubTab === subTab.id ? 0 : -1}
onClick={() => setActiveSubTab(subTab.id)}
className={cn(
"flex items-center gap-2 px-4 py-2 rounded-md font-medium transition-all text-sm",
activeSubTab === subTab.id
? "bg-white dark:bg-white/10 text-text-main shadow-sm"
: "text-text-muted hover:text-text-main"
)}
>
<span className="material-symbols-outlined text-[18px]" aria-hidden="true">
{subTab.icon}
</span>
<span className="whitespace-nowrap">
{subTab.id === "http" ? t("proxy") : t(subTab.labelKey)}
</span>
</button>
))}
</div>
</div>

<div
role="tabpanel"
aria-label={t(subTabs.find((t) => t.id === activeSubTab)?.labelKey || "proxy")}
>
{activeSubTab === "http" && (
<div className="flex flex-col gap-6">
<ProxyTab />
</div>
)}

{activeSubTab === "mitm" && (
<div className="flex flex-col gap-6">
<MitmProxyTab />
</div>
)}

{activeSubTab === "oneproxy" && (
<div className="flex flex-col gap-6">
<OneproxyTab />
</div>
)}
</div>
</div>
</div>
);
}
3 changes: 0 additions & 3 deletions src/lib/localDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,6 @@ export {
setReasoningCache,
getReasoningCache,
deleteReasoningCache,
cleanupExpiredReasoning,
getReasoningCacheStats,
getReasoningCacheEntries,
clearAllReasoningCache,
} from "./db/reasoningCache";

Expand Down
2 changes: 2 additions & 0 deletions src/shared/constants/sidebarVisibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const HIDEABLE_SIDEBAR_ITEM_IDS = [
"audit",
"webhooks",
"health",
"proxy",
"settings",
"docs",
"issues",
Expand Down Expand Up @@ -109,6 +110,7 @@ const SYSTEM_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [
{ id: "audit", href: "/dashboard/audit", i18nKey: "auditLog", icon: "policy" },
{ id: "webhooks", href: "/dashboard/webhooks", i18nKey: "webhooks", icon: "webhook" },
{ id: "health", href: "/dashboard/health", i18nKey: "health", icon: "health_and_safety" },
{ id: "proxy", href: "/dashboard/system/proxy", i18nKey: "proxy", icon: "dns" },
{ id: "settings", href: "/dashboard/settings", i18nKey: "settings", icon: "settings" },
];

Expand Down
4 changes: 1 addition & 3 deletions src/shared/schemas/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ export const comboSchema = z.object({
name: z.string().min(1, "Combo name is required").max(100),
model: z.string().min(1, "Model pattern is required"),
endpoint: z.enum(["chat", "embeddings", "images"]).default("chat"),
strategy: z
.enum(ROUTING_STRATEGY_VALUES)
.default("priority"),
strategy: z.enum(ROUTING_STRATEGY_VALUES).default("priority"),
nodes: z.array(comboNodeSchema).min(1, "At least one node is required"),
isActive: z.boolean().default(true),
maxRetries: z.number().int().min(0).max(10).default(2),
Expand Down
5 changes: 4 additions & 1 deletion tests/unit/combo-strategies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ test("auto strategy handles null and empty prompt edge cases without throwing",
});

assert.equal(
await selectedModelFor(combo, { model: combo.name, messages: [{ role: "user", content: null }] }),
await selectedModelFor(combo, {
model: combo.name,
messages: [{ role: "user", content: null }],
}),
"openai/gpt-4"
);
assert.equal(await selectedModelFor(combo, { model: combo.name, messages: [] }), "openai/gpt-4");
Expand Down
19 changes: 15 additions & 4 deletions tests/unit/executor-codex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,12 +527,23 @@ test("CodexExecutor.transformRequest strips raw internal assistant commentary wi
true
);
assert.equal(
result.input.some((item) => JSON.stringify(item).includes("Visible assistant history without phase")),
result.input.some((item) =>
JSON.stringify(item).includes("Visible assistant history without phase")
),
true
);
assert.equal(
result.input.some((item) => item.type === "reasoning"),
true
);
assert.equal(
result.input.some((item) => item.type === "function_call"),
true
);
assert.equal(
result.input.some((item) => item.type === "function_call_output"),
true
);
assert.equal(result.input.some((item) => item.type === "reasoning"), true);
assert.equal(result.input.some((item) => item.type === "function_call"), true);
assert.equal(result.input.some((item) => item.type === "function_call_output"), true);
});

test("CodexExecutor.transformRequest strips internal assistant commentary before mapping messages to input", () => {
Expand Down