From 930e9170925a2d014362545987a4ba8638846c75 Mon Sep 17 00:00:00 2001 From: decolua Date: Sun, 22 Feb 2026 11:30:43 +0700 Subject: [PATCH] chore: update version and enhance provider model configurations. --- open-sse/config/providerModels.js | 9 ++-- package.json | 2 +- .../usage/components/ProviderTopology.js | 48 ++++++------------- src/mitm/server.js | 1 + src/shared/constants/cliTools.js | 10 ++-- src/shared/constants/pricing.js | 7 +++ src/shared/services/initializeApp.js | 22 ++++++--- 7 files changed, 46 insertions(+), 53 deletions(-) diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.js index 20ebb4c..a141f19 100644 --- a/open-sse/config/providerModels.js +++ b/open-sse/config/providerModels.js @@ -60,13 +60,10 @@ export const PROVIDER_MODELS = { { id: "glm-5", name: "GLM 5" }, ], ag: [ // Antigravity - special case: models call different backends - { id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 Thinking" }, - { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, - { id: "gemini-3-1-pro-high", name: "Gemini 3.1 Pro High" }, - { id: "gemini-3-1-pro-low", name: "Gemini 3.1 Pro Low" }, - { id: "gemini-3-pro-high", name: "Gemini 3 Pro High" }, - { id: "gemini-3-pro-low", name: "Gemini 3 Pro Low" }, { id: "gemini-3-flash", name: "Gemini 3 Flash" }, + { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, + { id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 Thinking" }, + { id: "gpt-oss-120b-medium", name: "GPT OSS 120B Medium" }, ], gh: [ // GitHub Copilot - OpenAI models { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" }, diff --git a/package.json b/package.json index b61fa59..eb0a01b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "9router-app", - "version": "0.2.80", + "version": "0.2.91", "description": "9Router web dashboard", "private": true, "scripts": { diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js b/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js index 42b0815..f6486db 100644 --- a/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js +++ b/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js @@ -1,13 +1,11 @@ "use client"; -import { useMemo, useState, useCallback, useRef, useEffect } from "react"; +import { useMemo, useState, useCallback, useRef } from "react"; import PropTypes from "prop-types"; import { ReactFlow, Handle, Position, - useNodesState, - useEdgesState, } from "@xyflow/react"; import "@xyflow/react/dist/style.css"; import { AI_PROVIDERS } from "@/shared/constants/providers"; @@ -193,46 +191,30 @@ function buildLayout(providers, activeSet, lastSet, errorSet) { } export default function ProviderTopology({ providers = [], activeRequests = [], lastProvider = "", errorProvider = "" }) { - const activeSet = useMemo( - () => new Set(activeRequests.map((r) => r.provider?.toLowerCase()).filter(Boolean)), + // Serialize to stable string keys so useMemo only re-runs when values actually change + const activeKey = useMemo( + () => activeRequests.map((r) => r.provider?.toLowerCase()).filter(Boolean).sort().join(","), [activeRequests] ); + const lastKey = lastProvider?.toLowerCase() || ""; + const errorKey = errorProvider?.toLowerCase() || ""; - const lastSet = useMemo( - () => new Set(lastProvider ? [lastProvider.toLowerCase()] : []), - [lastProvider] + const activeSet = useMemo(() => new Set(activeKey ? activeKey.split(",") : []), [activeKey]); + const lastSet = useMemo(() => new Set(lastKey ? [lastKey] : []), [lastKey]); + const errorSet = useMemo(() => new Set(errorKey ? [errorKey] : []), [errorKey]); + + const { nodes, edges } = useMemo( + () => buildLayout(providers, activeSet, lastSet, errorSet), + [providers, activeKey, lastKey, errorKey] ); - const errorSet = useMemo( - () => new Set(errorProvider ? [errorProvider.toLowerCase()] : []), - [errorProvider] - ); - - // Stable key for providers list — only changes when provider set changes + // Stable key — only remount when provider list changes const providersKey = useMemo( () => providers.map((p) => p.provider).sort().join(","), [providers] ); - const { nodes: layoutNodes, edges: layoutEdges } = useMemo( - () => buildLayout(providers, activeSet, lastSet, errorSet), - [providers, activeSet, lastSet, errorSet] - ); - - const [nodes, setNodes, onNodesChange] = useNodesState(layoutNodes); - const [edges, setEdges, onEdgesChange] = useEdgesState(layoutEdges); const rfInstance = useRef(null); - - // Sync nodes/edges when data changes - useEffect(() => { - setNodes(layoutNodes); - setEdges(layoutEdges); - // Re-fit view after update - if (rfInstance.current) { - setTimeout(() => rfInstance.current.fitView({ padding: 0.3 }), 50); - } - }, [layoutNodes, layoutEdges, setNodes, setEdges]); - const onInit = useCallback((instance) => { rfInstance.current = instance; setTimeout(() => instance.fitView({ padding: 0.3 }), 50); @@ -249,8 +231,6 @@ export default function ProviderTopology({ providers = [], activeRequests = [], key={providersKey} nodes={nodes} edges={edges} - onNodesChange={onNodesChange} - onEdgesChange={onEdgesChange} nodeTypes={nodeTypes} fitView fitViewOptions={{ padding: 0.3 }} diff --git a/src/mitm/server.js b/src/mitm/server.js index 8b168d7..60121f0 100644 --- a/src/mitm/server.js +++ b/src/mitm/server.js @@ -186,6 +186,7 @@ const server = https.createServer(sslOptions, async (req, res) => { } const model = extractModel(bodyBuffer); + console.log(`📡 ${model} (passthrough)`); const mappedModel = getMappedModel(model); if (!mappedModel) { diff --git a/src/shared/constants/cliTools.js b/src/shared/constants/cliTools.js index c9ba947..e96d7f9 100644 --- a/src/shared/constants/cliTools.js +++ b/src/shared/constants/cliTools.js @@ -128,14 +128,14 @@ export const CLI_TOOLS = { color: "#4285F4", description: "Google Antigravity IDE with MITM", configType: "mitm", - modelAliases: ["claude-opus-4-6-thinking", "claude-sonnet-4-6-thinking", "gemini-3-pro-high"], + modelAliases: ["claude-opus-4-6-thinking", "claude-sonnet-4-6", "gemini-3-flash", "gpt-oss-120b-medium", "gemini-3-pro-high", "gemini-3-pro-low"], defaultModels: [ - { id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 Thinking", alias: "claude-opus-4-6-thinking" }, - { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", alias: "claude-sonnet-4-6" }, - { id: "gemini-3-1-pro-high", name: "Gemini 3.1 Pro High", alias: "gemini-3-1-pro-high" }, - { id: "gemini-3-1-pro-low", name: "Gemini 3.1 Pro Low", alias: "gemini-3-1-pro-low" }, { id: "gemini-3-pro-high", name: "Gemini 3 Pro High", alias: "gemini-3-pro-high" }, + { id: "gemini-3-pro-low", name: "Gemini 3 Pro Low", alias: "gemini-3-pro-low" }, { id: "gemini-3-flash", name: "Gemini 3 Flash", alias: "gemini-3-flash" }, + { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", alias: "claude-sonnet-4-6" }, + { id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 Thinking", alias: "claude-opus-4-6-thinking" }, + { id: "gpt-oss-120b-medium", name: "GPT OSS 120B Medium", alias: "gpt-oss-120b-medium" }, ], }, // HIDDEN: gemini-cli diff --git a/src/shared/constants/pricing.js b/src/shared/constants/pricing.js index 22a9f47..f2a09d3 100644 --- a/src/shared/constants/pricing.js +++ b/src/shared/constants/pricing.js @@ -355,6 +355,13 @@ export const DEFAULT_PRICING = { cached: 0.50, reasoning: 37.50, cache_creation: 5.00 + }, + "gpt-oss-120b-medium": { + input: 0.50, + output: 2.00, + cached: 0.25, + reasoning: 3.00, + cache_creation: 0.50 } }, diff --git a/src/shared/services/initializeApp.js b/src/shared/services/initializeApp.js index 91684bf..e9c9fa0 100644 --- a/src/shared/services/initializeApp.js +++ b/src/shared/services/initializeApp.js @@ -2,6 +2,11 @@ import { cleanupProviderConnections, getSettings } from "@/lib/localDb"; import { enableTunnel } from "@/lib/tunnel/tunnelManager"; import { killCloudflared, isCloudflaredRunning, ensureCloudflared } from "@/lib/tunnel/cloudflared"; +// Multiple modules register SIGINT/SIGTERM handlers legitimately +process.setMaxListeners(20); + +let signalHandlersRegistered = false; + /** * Initialize app on startup * - Cleanup stale data @@ -24,13 +29,16 @@ export async function initializeApp() { } } - // Kill cloudflared on process exit - const cleanup = () => { - killCloudflared(); - process.exit(); - }; - process.on("SIGINT", cleanup); - process.on("SIGTERM", cleanup); + // Kill cloudflared on process exit (register once only) + if (!signalHandlersRegistered) { + const cleanup = () => { + killCloudflared(); + process.exit(); + }; + process.on("SIGINT", cleanup); + process.on("SIGTERM", cleanup); + signalHandlersRegistered = true; + } // Pre-download cloudflared binary in background ensureCloudflared().catch(() => {});