synapse-gateway 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +385 -0
- package/bin/synapse.js +242 -0
- package/docs/PLAN.md +1723 -0
- package/docs/PRD.md +1799 -0
- package/drizzle.config.ts +12 -0
- package/next.config.ts +8 -0
- package/package.json +82 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/src/app/api/analytics/cost/route.ts +13 -0
- package/src/app/api/analytics/usage/route.ts +16 -0
- package/src/app/api/auth/login/route.ts +42 -0
- package/src/app/api/cache/route.ts +19 -0
- package/src/app/api/dashboard/route.ts +35 -0
- package/src/app/api/distill/route.ts +10 -0
- package/src/app/api/events/route.ts +54 -0
- package/src/app/api/health/route.ts +10 -0
- package/src/app/api/intelligence/forensics/route.ts +23 -0
- package/src/app/api/intelligence/neural-router/route.ts +23 -0
- package/src/app/api/keys/route.ts +34 -0
- package/src/app/api/mcp/route.ts +49 -0
- package/src/app/api/memory/route.ts +10 -0
- package/src/app/api/models/benchmark/route.ts +13 -0
- package/src/app/api/models/route.ts +39 -0
- package/src/app/api/namespace/route.ts +25 -0
- package/src/app/api/plugins/route.ts +41 -0
- package/src/app/api/providers/accounts/route.ts +91 -0
- package/src/app/api/providers/fetch-models/route.ts +52 -0
- package/src/app/api/providers/health/route.ts +10 -0
- package/src/app/api/providers/route.ts +46 -0
- package/src/app/api/routes/pipeline/route.ts +20 -0
- package/src/app/api/settings/route.ts +33 -0
- package/src/app/api/skills/route.ts +39 -0
- package/src/app/api/v1/chat/completions/route.ts +156 -0
- package/src/app/api/v1/models/route.ts +44 -0
- package/src/app/dashboard/intelligence/loading.tsx +14 -0
- package/src/app/dashboard/intelligence/page.tsx +125 -0
- package/src/app/dashboard/layout.tsx +143 -0
- package/src/app/dashboard/loading.tsx +17 -0
- package/src/app/dashboard/memory/loading.tsx +15 -0
- package/src/app/dashboard/memory/page.tsx +71 -0
- package/src/app/dashboard/models/loading.tsx +13 -0
- package/src/app/dashboard/models/page.tsx +107 -0
- package/src/app/dashboard/page.tsx +183 -0
- package/src/app/dashboard/playground/loading.tsx +17 -0
- package/src/app/dashboard/playground/page.tsx +212 -0
- package/src/app/dashboard/providers/loading.tsx +15 -0
- package/src/app/dashboard/providers/page.tsx +248 -0
- package/src/app/dashboard/routes/loading.tsx +15 -0
- package/src/app/dashboard/routes/page.tsx +72 -0
- package/src/app/dashboard/settings/loading.tsx +20 -0
- package/src/app/dashboard/settings/page.tsx +208 -0
- package/src/app/dashboard/skills/loading.tsx +26 -0
- package/src/app/dashboard/skills/page.tsx +137 -0
- package/src/app/dashboard/vault/loading.tsx +18 -0
- package/src/app/dashboard/vault/page.tsx +139 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +59 -0
- package/src/app/layout.tsx +32 -0
- package/src/app/login/page.tsx +87 -0
- package/src/app/page.tsx +5 -0
- package/src/components/ui/badge.tsx +32 -0
- package/src/components/ui/button.tsx +38 -0
- package/src/components/ui/card.tsx +50 -0
- package/src/components/ui/error-boundary.tsx +47 -0
- package/src/components/ui/index.ts +11 -0
- package/src/components/ui/input.tsx +26 -0
- package/src/components/ui/select.tsx +24 -0
- package/src/components/ui/skeleton.tsx +53 -0
- package/src/components/ui/toast.tsx +51 -0
- package/src/instrumentation.ts +6 -0
- package/src/lib/__tests__/auth.test.ts +42 -0
- package/src/lib/__tests__/format.test.ts +94 -0
- package/src/lib/__tests__/namespace.test.ts +102 -0
- package/src/lib/__tests__/squeezer.test.ts +93 -0
- package/src/lib/__tests__/utils.test.ts +28 -0
- package/src/lib/analytics/index.ts +187 -0
- package/src/lib/auth/guard.tsx +71 -0
- package/src/lib/auth/index.ts +105 -0
- package/src/lib/auth/middleware.ts +64 -0
- package/src/lib/benchmark/index.ts +137 -0
- package/src/lib/bootstrap.ts +122 -0
- package/src/lib/cache/index.ts +1 -0
- package/src/lib/cache/semantic.ts +211 -0
- package/src/lib/config/defaults.ts +61 -0
- package/src/lib/config/index.ts +72 -0
- package/src/lib/config/schema.ts +63 -0
- package/src/lib/db/index.ts +22 -0
- package/src/lib/db/migrate.ts +327 -0
- package/src/lib/db/schema.ts +303 -0
- package/src/lib/distiller/index.ts +331 -0
- package/src/lib/fallback/index.ts +153 -0
- package/src/lib/forensics/index.ts +188 -0
- package/src/lib/format/anthropic.ts +139 -0
- package/src/lib/format/gemini.ts +130 -0
- package/src/lib/format/index.ts +3 -0
- package/src/lib/format/openai.ts +78 -0
- package/src/lib/health/index.ts +158 -0
- package/src/lib/mcp/builtin.ts +83 -0
- package/src/lib/mcp/index.ts +1 -0
- package/src/lib/mcp/registry.ts +49 -0
- package/src/lib/memory/index.ts +3 -0
- package/src/lib/memory/store.ts +215 -0
- package/src/lib/memory/types.ts +56 -0
- package/src/lib/namespace/index.ts +89 -0
- package/src/lib/neural/features.ts +74 -0
- package/src/lib/neural/index.ts +85 -0
- package/src/lib/neural/strategies.ts +124 -0
- package/src/lib/pipeline/index.ts +84 -0
- package/src/lib/pipeline/types.ts +77 -0
- package/src/lib/plugins/builtin.ts +79 -0
- package/src/lib/plugins/index.ts +65 -0
- package/src/lib/prediction/index.ts +113 -0
- package/src/lib/providers/api-key/anthropic.ts +96 -0
- package/src/lib/providers/api-key/deepseek.ts +108 -0
- package/src/lib/providers/api-key/gemini.ts +112 -0
- package/src/lib/providers/api-key/openai.ts +122 -0
- package/src/lib/providers/api-key/openrouter.ts +112 -0
- package/src/lib/providers/base-adapter.ts +122 -0
- package/src/lib/providers/registry.ts +46 -0
- package/src/lib/providers/types.ts +121 -0
- package/src/lib/router/index.ts +82 -0
- package/src/lib/skills/forge.ts +57 -0
- package/src/lib/skills/index.ts +3 -0
- package/src/lib/skills/registry.ts +195 -0
- package/src/lib/skills/types.ts +44 -0
- package/src/lib/squeezer/index.ts +158 -0
- package/src/lib/utils/cn.ts +6 -0
- package/src/lib/utils/logger.ts +16 -0
- package/src/middleware.ts +60 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from 'drizzle-kit'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
schema: './src/lib/db/schema.ts',
|
|
5
|
+
out: './drizzle',
|
|
6
|
+
dialect: 'sqlite',
|
|
7
|
+
dbCredentials: {
|
|
8
|
+
url: process.env.DATA_DIR
|
|
9
|
+
? `${process.env.DATA_DIR}/db/adnify.db`
|
|
10
|
+
: `${process.env.HOME || process.env.USERPROFILE}/.adnify/db/adnify.db`,
|
|
11
|
+
},
|
|
12
|
+
})
|
package/next.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "synapse-gateway",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Synapse - AI Gateway & Intelligence Platform with Neural Router, Semantic Cache, Persistent Memory, Self-Learning, and 5+ AI Providers",
|
|
5
|
+
"keywords": ["ai", "ai-gateway", "llm", "openai", "anthropic", "gemini", "deepseek", "openrouter", "semantic-cache", "neural-router", "mcp", "sqlite"],
|
|
6
|
+
"author": "kevindoni",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/kevindoni/Synapse.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/kevindoni/Synapse#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/kevindoni/Synapse/issues"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.0.0"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"synapse": "./bin/synapse.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"bin/",
|
|
24
|
+
"src/",
|
|
25
|
+
"public/",
|
|
26
|
+
"docs/",
|
|
27
|
+
"next.config.ts",
|
|
28
|
+
"tsconfig.json",
|
|
29
|
+
"postcss.config.mjs",
|
|
30
|
+
"drizzle.config.ts"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "next dev",
|
|
34
|
+
"build": "next build",
|
|
35
|
+
"start": "node .next/standalone/server.js",
|
|
36
|
+
"lint": "eslint",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"test:e2e": "playwright test",
|
|
40
|
+
"test:all": "vitest run && playwright test"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@radix-ui/react-dialog": "^1.1.16",
|
|
44
|
+
"@radix-ui/react-dropdown-menu": "^2.1.17",
|
|
45
|
+
"@radix-ui/react-label": "^2.1.9",
|
|
46
|
+
"@radix-ui/react-select": "^2.3.0",
|
|
47
|
+
"@radix-ui/react-separator": "^1.1.9",
|
|
48
|
+
"@radix-ui/react-switch": "^1.3.0",
|
|
49
|
+
"@radix-ui/react-tabs": "^1.1.14",
|
|
50
|
+
"@radix-ui/react-tooltip": "^1.2.9",
|
|
51
|
+
"@tailwindcss/postcss": "^4",
|
|
52
|
+
"bcryptjs": "^3.0.3",
|
|
53
|
+
"better-sqlite3": "^12.10.0",
|
|
54
|
+
"class-variance-authority": "^0.7.1",
|
|
55
|
+
"clsx": "^2.1.1",
|
|
56
|
+
"drizzle-orm": "^0.45.2",
|
|
57
|
+
"jose": "^6.2.3",
|
|
58
|
+
"lucide-react": "^1.17.0",
|
|
59
|
+
"next": "16.2.7",
|
|
60
|
+
"pino": "^10.3.1",
|
|
61
|
+
"react": "19.2.4",
|
|
62
|
+
"react-dom": "19.2.4",
|
|
63
|
+
"tailwind-merge": "^3.6.0",
|
|
64
|
+
"tailwindcss": "^4",
|
|
65
|
+
"typescript": "^5",
|
|
66
|
+
"uuid": "^14.0.0",
|
|
67
|
+
"zod": "^4.4.3"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@playwright/test": "^1.60.0",
|
|
71
|
+
"@types/bcryptjs": "^2.4.6",
|
|
72
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
73
|
+
"@types/node": "^20",
|
|
74
|
+
"@types/react": "^19",
|
|
75
|
+
"@types/react-dom": "^19",
|
|
76
|
+
"@types/uuid": "^10.0.0",
|
|
77
|
+
"drizzle-kit": "^0.31.10",
|
|
78
|
+
"eslint": "^9",
|
|
79
|
+
"eslint-config-next": "16.2.7",
|
|
80
|
+
"vitest": "^4.1.8"
|
|
81
|
+
}
|
|
82
|
+
}
|
package/public/file.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
package/public/globe.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
package/public/next.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { costEngine } from '@/lib/prediction'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const [forecast, modelComparison] = await Promise.all([
|
|
6
|
+
costEngine.getForecast(),
|
|
7
|
+
costEngine.getModelCostComparison(),
|
|
8
|
+
])
|
|
9
|
+
return Response.json({ forecast, modelComparison })
|
|
10
|
+
} catch (err) {
|
|
11
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { analytics } from '@/lib/analytics'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const [stats, timeSeries, providerPerf, topModels] = await Promise.all([
|
|
6
|
+
analytics.getUsageStats(7),
|
|
7
|
+
analytics.getUsageTimeSeries(30),
|
|
8
|
+
analytics.getProviderPerformance(),
|
|
9
|
+
analytics.getTopModels(10),
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
return Response.json({ stats, timeSeries, providerPerformance: providerPerf, topModels })
|
|
13
|
+
} catch (err) {
|
|
14
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import type { NextRequest } from 'next/server'
|
|
3
|
+
import { signJwt, verifyJwt, generateApiKey, listApiKeys, revokeApiKey } from '@/lib/auth'
|
|
4
|
+
|
|
5
|
+
export async function POST(request: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
const { password } = await request.json()
|
|
8
|
+
const expected = process.env.SYNAPSE_PASSWORD || 'changeme'
|
|
9
|
+
|
|
10
|
+
if (password !== expected) {
|
|
11
|
+
return NextResponse.json({ error: 'Invalid password' }, { status: 401 })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const token = await signJwt({
|
|
15
|
+
sub: 'admin',
|
|
16
|
+
role: 'admin',
|
|
17
|
+
name: 'Admin',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return NextResponse.json({ token, user: { id: 'admin', role: 'admin', name: 'Admin' } })
|
|
21
|
+
} catch (err) {
|
|
22
|
+
return NextResponse.json({ error: (err as Error).message }, { status: 500 })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function GET(request: NextRequest) {
|
|
27
|
+
try {
|
|
28
|
+
const authHeader = request.headers.get('authorization')
|
|
29
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
30
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const payload = await verifyJwt(authHeader.slice(7))
|
|
34
|
+
if (!payload) {
|
|
35
|
+
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return NextResponse.json({ user: { id: payload.sub, role: payload.role, name: payload.name } })
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return NextResponse.json({ error: (err as Error).message }, { status: 500 })
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { semanticCacheStore } from '@/lib/cache'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const stats = await semanticCacheStore.getStats()
|
|
6
|
+
return Response.json({ stats })
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function DELETE() {
|
|
13
|
+
try {
|
|
14
|
+
await semanticCacheStore.clear()
|
|
15
|
+
return Response.json({ success: true })
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { db } from '@/lib/db'
|
|
2
|
+
import { requestLogs, providers, providerHealth, usageDaily } from '@/lib/db/schema'
|
|
3
|
+
import { desc, sql } from 'drizzle-orm'
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
try {
|
|
7
|
+
const [totalRequests] = await db.select({ count: sql<number>`count(*)` }).from(requestLogs)
|
|
8
|
+
const [todayUsage] = await db.select({
|
|
9
|
+
totalTokens: sql<number>`coalesce(sum(${usageDaily.totalInputTokens} + ${usageDaily.totalOutputTokens}), 0)`,
|
|
10
|
+
totalCost: sql<number>`coalesce(sum(${usageDaily.totalCost}), 0)`,
|
|
11
|
+
}).from(usageDaily).where(sql`${usageDaily.date} = date('now')`)
|
|
12
|
+
|
|
13
|
+
const recentRequests = await db.select().from(requestLogs).orderBy(desc(requestLogs.createdAt)).limit(20)
|
|
14
|
+
const allProviders = await db.select().from(providers)
|
|
15
|
+
const allHealth = await db.select().from(providerHealth)
|
|
16
|
+
|
|
17
|
+
const [cacheHits] = await db.select({ count: sql<number>`coalesce(sum(${requestLogs.cached}), 0)` }).from(requestLogs)
|
|
18
|
+
|
|
19
|
+
return Response.json({
|
|
20
|
+
stats: {
|
|
21
|
+
totalRequests: totalRequests?.count ?? 0,
|
|
22
|
+
todayTokens: todayUsage?.totalTokens ?? 0,
|
|
23
|
+
todayCost: Number(todayUsage?.totalCost ?? 0),
|
|
24
|
+
cacheHits: cacheHits?.count ?? 0,
|
|
25
|
+
},
|
|
26
|
+
recentRequests,
|
|
27
|
+
providers: allProviders.map((p) => ({
|
|
28
|
+
...p,
|
|
29
|
+
health: allHealth.find((h) => h.providerId === p.id) || null,
|
|
30
|
+
})),
|
|
31
|
+
})
|
|
32
|
+
} catch (err) {
|
|
33
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { experienceDistiller } from '@/lib/distiller'
|
|
2
|
+
|
|
3
|
+
export async function POST() {
|
|
4
|
+
try {
|
|
5
|
+
const result = await experienceDistiller.run()
|
|
6
|
+
return Response.json(result)
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export async function GET() {
|
|
2
|
+
const encoder = new TextEncoder()
|
|
3
|
+
|
|
4
|
+
const stream = new ReadableStream({
|
|
5
|
+
start(controller) {
|
|
6
|
+
const send = (event: string, data: unknown) => {
|
|
7
|
+
controller.enqueue(encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
send('connected', { message: 'Synapse SSE connected', timestamp: Date.now() })
|
|
11
|
+
|
|
12
|
+
const heartbeat = setInterval(() => {
|
|
13
|
+
try {
|
|
14
|
+
send('heartbeat', { timestamp: Date.now() })
|
|
15
|
+
} catch {
|
|
16
|
+
clearInterval(heartbeat)
|
|
17
|
+
clearInterval(statsInterval)
|
|
18
|
+
}
|
|
19
|
+
}, 30000)
|
|
20
|
+
|
|
21
|
+
const statsInterval = setInterval(async () => {
|
|
22
|
+
try {
|
|
23
|
+
send('stats', {
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
requestsPerMinute: Math.floor(Math.random() * 50) + 10,
|
|
26
|
+
tokensPerMinute: Math.floor(Math.random() * 5000) + 1000,
|
|
27
|
+
activeProviders: 5,
|
|
28
|
+
cacheHitRate: Math.random() * 0.3 + 0.1,
|
|
29
|
+
})
|
|
30
|
+
} catch {
|
|
31
|
+
clearInterval(statsInterval)
|
|
32
|
+
clearInterval(heartbeat)
|
|
33
|
+
}
|
|
34
|
+
}, 5000)
|
|
35
|
+
|
|
36
|
+
send('stats', { timestamp: Date.now(), requestsPerMinute: 0, tokensPerMinute: 0, activeProviders: 5, cacheHitRate: 0 })
|
|
37
|
+
|
|
38
|
+
const originalClose = controller.close.bind(controller)
|
|
39
|
+
controller.close = () => {
|
|
40
|
+
clearInterval(heartbeat)
|
|
41
|
+
clearInterval(statsInterval)
|
|
42
|
+
originalClose()
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return new Response(stream, {
|
|
48
|
+
headers: {
|
|
49
|
+
'Content-Type': 'text/event-stream',
|
|
50
|
+
'Cache-Control': 'no-cache',
|
|
51
|
+
Connection: 'keep-alive',
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { forensicsEngine } from '@/lib/forensics'
|
|
2
|
+
|
|
3
|
+
export async function GET(request: Request) {
|
|
4
|
+
try {
|
|
5
|
+
const url = new URL(request.url)
|
|
6
|
+
const requestId = url.searchParams.get('request_id')
|
|
7
|
+
|
|
8
|
+
if (requestId) {
|
|
9
|
+
const report = await forensicsEngine.analyzeRequest(requestId)
|
|
10
|
+
if (!report) return Response.json({ error: 'Request not found' }, { status: 404 })
|
|
11
|
+
return Response.json(report)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const [failures, slow] = await Promise.all([
|
|
15
|
+
forensicsEngine.getRecentFailures(10),
|
|
16
|
+
forensicsEngine.getSlowRequests(3000, 10),
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
return Response.json({ recentFailures: failures, slowRequests: slow })
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { neuralRouter } from '@/lib/neural'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const stats = await neuralRouter.getAccuracyStats()
|
|
6
|
+
return Response.json({
|
|
7
|
+
defaultStrategy: neuralRouter.getDefaultStrategy(),
|
|
8
|
+
...stats,
|
|
9
|
+
})
|
|
10
|
+
} catch (err) {
|
|
11
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function PUT(request: Request) {
|
|
16
|
+
try {
|
|
17
|
+
const { strategy } = await request.json()
|
|
18
|
+
neuralRouter.setDefaultStrategy(strategy)
|
|
19
|
+
return Response.json({ success: true, strategy })
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateApiKey, listApiKeys, revokeApiKey } from '@/lib/auth'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const keys = await listApiKeys()
|
|
6
|
+
return Response.json({ keys })
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function POST(request: Request) {
|
|
13
|
+
try {
|
|
14
|
+
const { name } = await request.json()
|
|
15
|
+
if (!name) return Response.json({ error: 'name is required' }, { status: 400 })
|
|
16
|
+
|
|
17
|
+
const result = await generateApiKey(name)
|
|
18
|
+
return Response.json(result)
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function DELETE(request: Request) {
|
|
25
|
+
try {
|
|
26
|
+
const { id } = await request.json()
|
|
27
|
+
if (!id) return Response.json({ error: 'id is required' }, { status: 400 })
|
|
28
|
+
|
|
29
|
+
await revokeApiKey(id)
|
|
30
|
+
return Response.json({ success: true })
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { registerTool, registerResource, listTools, listResources, callTool, readResource } from '@/lib/mcp/registry'
|
|
2
|
+
import { analytics } from '@/lib/analytics'
|
|
3
|
+
import { healthChecker } from '@/lib/health'
|
|
4
|
+
import { semanticCacheStore } from '@/lib/cache'
|
|
5
|
+
import { memoryStore } from '@/lib/memory'
|
|
6
|
+
import { experienceDistiller } from '@/lib/distiller'
|
|
7
|
+
import { registry } from '@/lib/providers/registry'
|
|
8
|
+
|
|
9
|
+
let registered = false
|
|
10
|
+
function ensureRegistered() {
|
|
11
|
+
if (registered) return
|
|
12
|
+
registered = true
|
|
13
|
+
|
|
14
|
+
registerTool({ name: 'get_usage_stats', description: 'Get usage statistics', inputSchema: { type: 'object', properties: { days: { type: 'number' } } }, handler: async (args) => analytics.getUsageStats((args.days as number) || 7) })
|
|
15
|
+
registerTool({ name: 'get_health_status', description: 'Get provider health', inputSchema: { type: 'object', properties: {} }, handler: async () => healthChecker.getProviderHealth() })
|
|
16
|
+
registerTool({ name: 'get_cache_stats', description: 'Get cache statistics', inputSchema: { type: 'object', properties: {} }, handler: async () => semanticCacheStore.getStats() })
|
|
17
|
+
registerTool({ name: 'search_memory', description: 'Search memory', inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] }, handler: async (args) => memoryStore.search(args.query as string) })
|
|
18
|
+
registerTool({ name: 'clear_cache', description: 'Clear cache', inputSchema: { type: 'object', properties: {} }, handler: async () => { await semanticCacheStore.clear(); return { success: true } } })
|
|
19
|
+
registerTool({ name: 'trigger_distillation', description: 'Run distillation', inputSchema: { type: 'object', properties: {} }, handler: async () => experienceDistiller.run() })
|
|
20
|
+
registerResource({ uri: 'synapse://config', name: 'Configuration', description: 'Current config', mimeType: 'application/json', read: async () => JSON.stringify({ version: '2.0.0' }) })
|
|
21
|
+
registerResource({ uri: 'synapse://providers', name: 'Providers', description: 'Provider list', mimeType: 'application/json', read: async () => JSON.stringify(registry.list().map((a) => ({ id: a.info.id, name: a.info.name }))) })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function GET() {
|
|
25
|
+
ensureRegistered()
|
|
26
|
+
return Response.json({ tools: listTools(), resources: listResources() })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function POST(request: Request) {
|
|
30
|
+
ensureRegistered()
|
|
31
|
+
try {
|
|
32
|
+
const { method, params } = await request.json()
|
|
33
|
+
|
|
34
|
+
if (method === 'tools/list') return Response.json({ tools: listTools() })
|
|
35
|
+
if (method === 'tools/call') {
|
|
36
|
+
const result = await callTool(params.name, params.arguments || {})
|
|
37
|
+
return Response.json({ content: [{ type: 'text', text: JSON.stringify(result) }] })
|
|
38
|
+
}
|
|
39
|
+
if (method === 'resources/list') return Response.json({ resources: listResources() })
|
|
40
|
+
if (method === 'resources/read') {
|
|
41
|
+
const content = await readResource(params.uri)
|
|
42
|
+
return Response.json({ contents: [{ uri: params.uri, mimeType: 'application/json', text: content }] })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return Response.json({ error: `Unknown method: ${method}` }, { status: 400 })
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { memoryStore } from '@/lib/memory'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const stats = await memoryStore.getStats()
|
|
6
|
+
return Response.json({ stats })
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { benchmarkEngine } from '@/lib/benchmark'
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
try {
|
|
5
|
+
const [comparisons, recent] = await Promise.all([
|
|
6
|
+
benchmarkEngine.compareModels(),
|
|
7
|
+
benchmarkEngine.getRecentBenchmarks(20),
|
|
8
|
+
])
|
|
9
|
+
return Response.json({ comparisons, recent })
|
|
10
|
+
} catch (err) {
|
|
11
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { db } from '@/lib/db'
|
|
2
|
+
import { models } from '@/lib/db/schema'
|
|
3
|
+
import { eq } from 'drizzle-orm'
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
try {
|
|
7
|
+
const allModels = await db.select().from(models)
|
|
8
|
+
return Response.json({ models: allModels })
|
|
9
|
+
} catch (err) {
|
|
10
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(request: Request) {
|
|
15
|
+
try {
|
|
16
|
+
const body = await request.json()
|
|
17
|
+
const { id, providerId, name, displayName, pricingTier, costPer1mInput, costPer1mOutput, contextWindow, capabilities } = body
|
|
18
|
+
|
|
19
|
+
if (!id || !providerId || !name) {
|
|
20
|
+
return Response.json({ error: 'id, providerId, name are required' }, { status: 400 })
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await db.insert(models).values({
|
|
24
|
+
id,
|
|
25
|
+
providerId,
|
|
26
|
+
name,
|
|
27
|
+
displayName: displayName || null,
|
|
28
|
+
pricingTier: pricingTier || null,
|
|
29
|
+
costPer1mInput: costPer1mInput ?? null,
|
|
30
|
+
costPer1mOutput: costPer1mOutput ?? null,
|
|
31
|
+
contextWindow: contextWindow ?? null,
|
|
32
|
+
capabilities: capabilities ? JSON.stringify(capabilities) : '[]',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return Response.json({ success: true, id })
|
|
36
|
+
} catch (err) {
|
|
37
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { namespaceResolver } from '@/lib/namespace'
|
|
2
|
+
|
|
3
|
+
export async function GET(request: Request) {
|
|
4
|
+
try {
|
|
5
|
+
const url = new URL(request.url)
|
|
6
|
+
const model = url.searchParams.get('model')
|
|
7
|
+
const task = url.searchParams.get('task')
|
|
8
|
+
|
|
9
|
+
const aliases = namespaceResolver.listAliases()
|
|
10
|
+
|
|
11
|
+
if (model) {
|
|
12
|
+
const resolved = namespaceResolver.resolve(model)
|
|
13
|
+
return Response.json({ input: model, resolved })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (task) {
|
|
17
|
+
const models = namespaceResolver.resolveForTask(task as 'code' | 'chat' | 'reason')
|
|
18
|
+
return Response.json({ task, models })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Response.json({ aliases })
|
|
22
|
+
} catch (err) {
|
|
23
|
+
return Response.json({ error: (err as Error).message }, { status: 500 })
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { pluginManager } from '@/lib/plugins/index'
|
|
2
|
+
|
|
3
|
+
const PII_PATTERNS = [
|
|
4
|
+
/\b\d{3}[-.]?\d{2}[-.]?\d{4}\b/g,
|
|
5
|
+
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
6
|
+
/\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
let registered = false
|
|
10
|
+
function ensureRegistered() {
|
|
11
|
+
if (registered) return
|
|
12
|
+
registered = true
|
|
13
|
+
|
|
14
|
+
pluginManager.register({
|
|
15
|
+
name: 'pii-redactor',
|
|
16
|
+
description: 'Redacts PII (SSN, email, phone)',
|
|
17
|
+
phase: 'pre_request',
|
|
18
|
+
execute: async (ctx) => {
|
|
19
|
+
let content = ctx.content
|
|
20
|
+
for (const pattern of PII_PATTERNS) {
|
|
21
|
+
content = content.replace(pattern, '[REDACTED]')
|
|
22
|
+
}
|
|
23
|
+
return { modified: content !== ctx.content, content }
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
pluginManager.register({
|
|
27
|
+
name: 'response-validator',
|
|
28
|
+
description: 'Validates response quality',
|
|
29
|
+
phase: 'post_response',
|
|
30
|
+
execute: async (ctx) => {
|
|
31
|
+
const valid = ctx.content.trim().length > 0
|
|
32
|
+
return { modified: false, content: ctx.content, metadata: { validation: { valid } } }
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function GET() {
|
|
38
|
+
ensureRegistered()
|
|
39
|
+
const plugins = pluginManager.list()
|
|
40
|
+
return Response.json({ plugins })
|
|
41
|
+
}
|