From 53fd36e4f2397806952d835f489ca833a8592d1e Mon Sep 17 00:00:00 2001 From: Paijo <14921983+oyi77@users.noreply.github.com> Date: Mon, 4 May 2026 02:16:18 +0700 Subject: [PATCH] =?UTF-8?q?feat(proxy):=20move=20proxy=20configuration=20t?= =?UTF-8?q?o=20dedicated=20System=20=E2=86=92=20Proxy=20page=20(#1907)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrated into release/v3.7.9 --- open-sse/mcp-server/schemas/tools.ts | 4 +- open-sse/mcp-server/tools/advancedTools.ts | 4 +- open-sse/services/autoCombo/index.ts | 6 +- open-sse/services/combo.ts | 13 ++- .../dashboard/compression/page.tsx | 5 ++ .../settings/components/ComboDefaultsTab.tsx | 3 +- .../(dashboard)/dashboard/settings/page.tsx | 6 -- .../dashboard/system/proxy/page.tsx | 79 +++++++++++++++++++ src/lib/localDb.ts | 3 - src/shared/constants/sidebarVisibility.ts | 2 + src/shared/schemas/validation.ts | 4 +- tests/unit/combo-strategies.test.ts | 5 +- tests/unit/executor-codex.test.ts | 19 ++++- 13 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 src/app/(dashboard)/dashboard/system/proxy/page.tsx diff --git a/open-sse/mcp-server/schemas/tools.ts b/open-sse/mcp-server/schemas/tools.ts index 680639c02e..7f829b1529 100644 --- a/open-sse/mcp-server/schemas/tools.ts +++ b/open-sse/mcp-server/schemas/tools.ts @@ -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() diff --git a/open-sse/mcp-server/tools/advancedTools.ts b/open-sse/mcp-server/tools/advancedTools.ts index 09041fa941..723a061925 100644 --- a/open-sse/mcp-server/tools/advancedTools.ts +++ b/open-sse/mcp-server/tools/advancedTools.ts @@ -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, }, }; diff --git a/open-sse/services/autoCombo/index.ts b/open-sse/services/autoCombo/index.ts index 4040c693f7..062dbccacf 100644 --- a/open-sse/services/autoCombo/index.ts +++ b/open-sse/services/autoCombo/index.ts @@ -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"; diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index df0c5a741f..8bd9efa5e9 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -665,14 +665,18 @@ function sortTargetsByContextSize(targets: ResolvedComboTarget[]) { .filter((target): target is ResolvedComboTarget => target !== null); } -function getP2CTargetScore(target: ResolvedComboTarget, metrics: ReturnType): number { +function getP2CTargetScore( + target: ResolvedComboTarget, + metrics: ReturnType +): 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; } @@ -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}`); diff --git a/src/app/(dashboard)/dashboard/compression/page.tsx b/src/app/(dashboard)/dashboard/compression/page.tsx index a347872010..63d5b50ca1 100644 --- a/src/app/(dashboard)/dashboard/compression/page.tsx +++ b/src/app/(dashboard)/dashboard/compression/page.tsx @@ -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"); } diff --git a/src/app/(dashboard)/dashboard/settings/components/ComboDefaultsTab.tsx b/src/app/(dashboard)/dashboard/settings/components/ComboDefaultsTab.tsx index 4f0036b02e..7146fa8a9b 100644 --- a/src/app/(dashboard)/dashboard/settings/components/ComboDefaultsTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/ComboDefaultsTab.tsx @@ -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 ?? diff --git a/src/app/(dashboard)/dashboard/settings/page.tsx b/src/app/(dashboard)/dashboard/settings/page.tsx index 211f678e25..a2c6af481c 100644 --- a/src/app/(dashboard)/dashboard/settings/page.tsx +++ b/src/app/(dashboard)/dashboard/settings/page.tsx @@ -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"; @@ -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 = [ @@ -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" }, ]; @@ -138,12 +135,9 @@ export default function SettingsPage() { {activeTab === "resilience" && } - {activeTab === "mitm" && } - {activeTab === "advanced" && (
-
)} diff --git a/src/app/(dashboard)/dashboard/system/proxy/page.tsx b/src/app/(dashboard)/dashboard/system/proxy/page.tsx new file mode 100644 index 0000000000..9ddf0c390c --- /dev/null +++ b/src/app/(dashboard)/dashboard/system/proxy/page.tsx @@ -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 ( +
+
+
+
+ {subTabs.map((subTab) => ( + + ))} +
+
+ +
t.id === activeSubTab)?.labelKey || "proxy")} + > + {activeSubTab === "http" && ( +
+ +
+ )} + + {activeSubTab === "mitm" && ( +
+ +
+ )} + + {activeSubTab === "oneproxy" && ( +
+ +
+ )} +
+
+
+ ); +} diff --git a/src/lib/localDb.ts b/src/lib/localDb.ts index 73da21575f..214810e8c1 100755 --- a/src/lib/localDb.ts +++ b/src/lib/localDb.ts @@ -341,9 +341,6 @@ export { setReasoningCache, getReasoningCache, deleteReasoningCache, - cleanupExpiredReasoning, - getReasoningCacheStats, - getReasoningCacheEntries, clearAllReasoningCache, } from "./db/reasoningCache"; diff --git a/src/shared/constants/sidebarVisibility.ts b/src/shared/constants/sidebarVisibility.ts index fdebe48cea..300a75d86c 100644 --- a/src/shared/constants/sidebarVisibility.ts +++ b/src/shared/constants/sidebarVisibility.ts @@ -24,6 +24,7 @@ export const HIDEABLE_SIDEBAR_ITEM_IDS = [ "audit", "webhooks", "health", + "proxy", "settings", "docs", "issues", @@ -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" }, ]; diff --git a/src/shared/schemas/validation.ts b/src/shared/schemas/validation.ts index 628aeaeaf6..b08b8d22f7 100644 --- a/src/shared/schemas/validation.ts +++ b/src/shared/schemas/validation.ts @@ -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), diff --git a/tests/unit/combo-strategies.test.ts b/tests/unit/combo-strategies.test.ts index 2f97b1a2de..65d62c88dd 100644 --- a/tests/unit/combo-strategies.test.ts +++ b/tests/unit/combo-strategies.test.ts @@ -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"); diff --git a/tests/unit/executor-codex.test.ts b/tests/unit/executor-codex.test.ts index 3474b527f5..e7bd377127 100644 --- a/tests/unit/executor-codex.test.ts +++ b/tests/unit/executor-codex.test.ts @@ -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", () => {