chore: update version and enhance provider model configurations.
This commit is contained in:
@@ -60,13 +60,10 @@ export const PROVIDER_MODELS = {
|
|||||||
{ id: "glm-5", name: "GLM 5" },
|
{ id: "glm-5", name: "GLM 5" },
|
||||||
],
|
],
|
||||||
ag: [ // Antigravity - special case: models call different backends
|
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: "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
|
gh: [ // GitHub Copilot - OpenAI models
|
||||||
{ id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" },
|
{ id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "9router-app",
|
"name": "9router-app",
|
||||||
"version": "0.2.80",
|
"version": "0.2.91",
|
||||||
"description": "9Router web dashboard",
|
"description": "9Router web dashboard",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo, useState, useCallback, useRef, useEffect } from "react";
|
import { useMemo, useState, useCallback, useRef } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {
|
import {
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
Handle,
|
Handle,
|
||||||
Position,
|
Position,
|
||||||
useNodesState,
|
|
||||||
useEdgesState,
|
|
||||||
} from "@xyflow/react";
|
} from "@xyflow/react";
|
||||||
import "@xyflow/react/dist/style.css";
|
import "@xyflow/react/dist/style.css";
|
||||||
import { AI_PROVIDERS } from "@/shared/constants/providers";
|
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 = "" }) {
|
export default function ProviderTopology({ providers = [], activeRequests = [], lastProvider = "", errorProvider = "" }) {
|
||||||
const activeSet = useMemo(
|
// Serialize to stable string keys so useMemo only re-runs when values actually change
|
||||||
() => new Set(activeRequests.map((r) => r.provider?.toLowerCase()).filter(Boolean)),
|
const activeKey = useMemo(
|
||||||
|
() => activeRequests.map((r) => r.provider?.toLowerCase()).filter(Boolean).sort().join(","),
|
||||||
[activeRequests]
|
[activeRequests]
|
||||||
);
|
);
|
||||||
|
const lastKey = lastProvider?.toLowerCase() || "";
|
||||||
|
const errorKey = errorProvider?.toLowerCase() || "";
|
||||||
|
|
||||||
const lastSet = useMemo(
|
const activeSet = useMemo(() => new Set(activeKey ? activeKey.split(",") : []), [activeKey]);
|
||||||
() => new Set(lastProvider ? [lastProvider.toLowerCase()] : []),
|
const lastSet = useMemo(() => new Set(lastKey ? [lastKey] : []), [lastKey]);
|
||||||
[lastProvider]
|
const errorSet = useMemo(() => new Set(errorKey ? [errorKey] : []), [errorKey]);
|
||||||
|
|
||||||
|
const { nodes, edges } = useMemo(
|
||||||
|
() => buildLayout(providers, activeSet, lastSet, errorSet),
|
||||||
|
[providers, activeKey, lastKey, errorKey]
|
||||||
);
|
);
|
||||||
|
|
||||||
const errorSet = useMemo(
|
// Stable key — only remount when provider list changes
|
||||||
() => new Set(errorProvider ? [errorProvider.toLowerCase()] : []),
|
|
||||||
[errorProvider]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Stable key for providers list — only changes when provider set changes
|
|
||||||
const providersKey = useMemo(
|
const providersKey = useMemo(
|
||||||
() => providers.map((p) => p.provider).sort().join(","),
|
() => providers.map((p) => p.provider).sort().join(","),
|
||||||
[providers]
|
[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);
|
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) => {
|
const onInit = useCallback((instance) => {
|
||||||
rfInstance.current = instance;
|
rfInstance.current = instance;
|
||||||
setTimeout(() => instance.fitView({ padding: 0.3 }), 50);
|
setTimeout(() => instance.fitView({ padding: 0.3 }), 50);
|
||||||
@@ -249,8 +231,6 @@ export default function ProviderTopology({ providers = [], activeRequests = [],
|
|||||||
key={providersKey}
|
key={providersKey}
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
onNodesChange={onNodesChange}
|
|
||||||
onEdgesChange={onEdgesChange}
|
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
fitView
|
fitView
|
||||||
fitViewOptions={{ padding: 0.3 }}
|
fitViewOptions={{ padding: 0.3 }}
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ const server = https.createServer(sslOptions, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const model = extractModel(bodyBuffer);
|
const model = extractModel(bodyBuffer);
|
||||||
|
console.log(`📡 ${model} (passthrough)`);
|
||||||
const mappedModel = getMappedModel(model);
|
const mappedModel = getMappedModel(model);
|
||||||
|
|
||||||
if (!mappedModel) {
|
if (!mappedModel) {
|
||||||
|
|||||||
@@ -128,14 +128,14 @@ export const CLI_TOOLS = {
|
|||||||
color: "#4285F4",
|
color: "#4285F4",
|
||||||
description: "Google Antigravity IDE with MITM",
|
description: "Google Antigravity IDE with MITM",
|
||||||
configType: "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: [
|
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-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: "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
|
// HIDDEN: gemini-cli
|
||||||
|
|||||||
@@ -355,6 +355,13 @@ export const DEFAULT_PRICING = {
|
|||||||
cached: 0.50,
|
cached: 0.50,
|
||||||
reasoning: 37.50,
|
reasoning: 37.50,
|
||||||
cache_creation: 5.00
|
cache_creation: 5.00
|
||||||
|
},
|
||||||
|
"gpt-oss-120b-medium": {
|
||||||
|
input: 0.50,
|
||||||
|
output: 2.00,
|
||||||
|
cached: 0.25,
|
||||||
|
reasoning: 3.00,
|
||||||
|
cache_creation: 0.50
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import { cleanupProviderConnections, getSettings } from "@/lib/localDb";
|
|||||||
import { enableTunnel } from "@/lib/tunnel/tunnelManager";
|
import { enableTunnel } from "@/lib/tunnel/tunnelManager";
|
||||||
import { killCloudflared, isCloudflaredRunning, ensureCloudflared } from "@/lib/tunnel/cloudflared";
|
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
|
* Initialize app on startup
|
||||||
* - Cleanup stale data
|
* - Cleanup stale data
|
||||||
@@ -24,13 +29,16 @@ export async function initializeApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill cloudflared on process exit
|
// Kill cloudflared on process exit (register once only)
|
||||||
const cleanup = () => {
|
if (!signalHandlersRegistered) {
|
||||||
killCloudflared();
|
const cleanup = () => {
|
||||||
process.exit();
|
killCloudflared();
|
||||||
};
|
process.exit();
|
||||||
process.on("SIGINT", cleanup);
|
};
|
||||||
process.on("SIGTERM", cleanup);
|
process.on("SIGINT", cleanup);
|
||||||
|
process.on("SIGTERM", cleanup);
|
||||||
|
signalHandlersRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Pre-download cloudflared binary in background
|
// Pre-download cloudflared binary in background
|
||||||
ensureCloudflared().catch(() => {});
|
ensureCloudflared().catch(() => {});
|
||||||
|
|||||||
Reference in New Issue
Block a user