ai-agent-router 0.1.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/.claude/commands/openspec/apply.md +23 -0
- package/.claude/commands/openspec/archive.md +27 -0
- package/.claude/commands/openspec/proposal.md +28 -0
- package/.claude/settings.local.json +12 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +228 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-311.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +238 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +61 -0
- package/.cursor/commands/openspec-apply.md +23 -0
- package/.cursor/commands/openspec-archive.md +27 -0
- package/.cursor/commands/openspec-proposal.md +28 -0
- package/.cursor/commands/ui-ux-pro-max.md +226 -0
- package/.eslintrc.json +3 -0
- package/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.shared/ui-ux-pro-max/scripts/core.py +238 -0
- package/.shared/ui-ux-pro-max/scripts/search.py +61 -0
- package/AGENTS.md +18 -0
- package/CLAUDE.md +18 -0
- package/IMPLEMENTATION.md +157 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/.next/types/app/api/config/route.js +52 -0
- package/dist/.next/types/app/api/gateway/[...path]/route.js +52 -0
- package/dist/.next/types/app/api/gateway/route.js +52 -0
- package/dist/.next/types/app/api/logs/route.js +52 -0
- package/dist/.next/types/app/api/models/route.js +52 -0
- package/dist/.next/types/app/api/providers/route.js +52 -0
- package/dist/.next/types/app/api/providers/test/route.js +52 -0
- package/dist/.next/types/app/api/service/start/route.js +52 -0
- package/dist/.next/types/app/api/service/status/route.js +52 -0
- package/dist/.next/types/app/api/service/stop/route.js +52 -0
- package/dist/.next/types/app/layout.js +22 -0
- package/dist/.next/types/app/logs/page.js +22 -0
- package/dist/.next/types/app/models/page.js +22 -0
- package/dist/.next/types/app/page.js +22 -0
- package/dist/.next/types/app/providers/page.js +22 -0
- package/dist/src/app/api/config/route.js +43 -0
- package/dist/src/app/api/gateway/[...path]/route.js +83 -0
- package/dist/src/app/api/gateway/route.js +63 -0
- package/dist/src/app/api/logs/route.js +34 -0
- package/dist/src/app/api/models/route.js +152 -0
- package/dist/src/app/api/providers/route.js +118 -0
- package/dist/src/app/api/providers/test/route.js +154 -0
- package/dist/src/app/api/service/start/route.js +55 -0
- package/dist/src/app/api/service/status/route.js +17 -0
- package/dist/src/app/api/service/stop/route.js +20 -0
- package/dist/src/app/components/ConfirmDialog.jsx +31 -0
- package/dist/src/app/components/Nav.jsx +45 -0
- package/dist/src/app/components/Toast.jsx +37 -0
- package/dist/src/app/components/ToastProvider.jsx +21 -0
- package/dist/src/app/layout.jsx +13 -0
- package/dist/src/app/logs/page.jsx +210 -0
- package/dist/src/app/models/page.jsx +291 -0
- package/dist/src/app/page.jsx +236 -0
- package/dist/src/app/providers/page.jsx +402 -0
- package/dist/src/cli/index.js +90 -0
- package/dist/src/db/database.js +69 -0
- package/dist/src/db/queries.js +261 -0
- package/dist/src/db/schema.js +67 -0
- package/dist/src/server/crypto.js +22 -0
- package/dist/src/server/gateway-server.js +200 -0
- package/dist/src/server/gateway.js +76 -0
- package/dist/src/server/logger.js +72 -0
- package/dist/src/server/providers/anthropic.js +52 -0
- package/dist/src/server/providers/gemini.js +64 -0
- package/dist/src/server/providers/index.js +16 -0
- package/dist/src/server/providers/openai.js +86 -0
- package/dist/src/server/providers/types.js +1 -0
- package/dist/src/server/service-manager.js +286 -0
- package/docs/TODO.md +19 -0
- package/next.config.js +7 -0
- package/openspec/AGENTS.md +456 -0
- package/openspec/changes/add-logging/proposal.md +18 -0
- package/openspec/changes/add-logging/specs/core/spec.md +21 -0
- package/openspec/changes/add-logging/tasks.md +16 -0
- package/openspec/changes/add-provider-test-connection/proposal.md +22 -0
- package/openspec/changes/add-provider-test-connection/specs/model-provider/spec.md +68 -0
- package/openspec/changes/add-provider-test-connection/tasks.md +31 -0
- package/openspec/changes/improve-gateway-startup/design.md +137 -0
- package/openspec/changes/improve-gateway-startup/proposal.md +33 -0
- package/openspec/changes/improve-gateway-startup/specs/api-gateway/spec.md +94 -0
- package/openspec/changes/improve-gateway-startup/specs/web-ui/spec.md +67 -0
- package/openspec/changes/improve-gateway-startup/tasks.md +47 -0
- package/openspec/changes/init-api-gateway/design.md +185 -0
- package/openspec/changes/init-api-gateway/proposal.md +30 -0
- package/openspec/changes/init-api-gateway/specs/api-gateway/spec.md +42 -0
- package/openspec/changes/init-api-gateway/specs/cli-tool/spec.md +40 -0
- package/openspec/changes/init-api-gateway/specs/model-management/spec.md +47 -0
- package/openspec/changes/init-api-gateway/specs/model-provider/spec.md +33 -0
- package/openspec/changes/init-api-gateway/specs/request-logging/spec.md +54 -0
- package/openspec/changes/init-api-gateway/specs/web-ui/spec.md +49 -0
- package/openspec/changes/init-api-gateway/tasks.md +84 -0
- package/openspec/project.md +58 -0
- package/package.json +51 -0
- package/postcss.config.js +6 -0
- package/src/app/api/config/route.ts +62 -0
- package/src/app/api/gateway/[...path]/route.ts +118 -0
- package/src/app/api/gateway/route.ts +77 -0
- package/src/app/api/logs/route.ts +48 -0
- package/src/app/api/models/route.ts +210 -0
- package/src/app/api/providers/route.ts +162 -0
- package/src/app/api/providers/test/route.ts +182 -0
- package/src/app/api/service/start/route.ts +73 -0
- package/src/app/api/service/status/route.ts +22 -0
- package/src/app/api/service/stop/route.ts +27 -0
- package/src/app/components/ConfirmDialog.tsx +63 -0
- package/src/app/components/Nav.tsx +66 -0
- package/src/app/components/Toast.tsx +61 -0
- package/src/app/components/ToastProvider.tsx +43 -0
- package/src/app/globals.css +71 -0
- package/src/app/layout.tsx +22 -0
- package/src/app/logs/page.tsx +261 -0
- package/src/app/models/page.tsx +500 -0
- package/src/app/page.tsx +742 -0
- package/src/app/providers/page.tsx +558 -0
- package/src/cli/index.ts +95 -0
- package/src/db/database.ts +125 -0
- package/src/db/queries.ts +339 -0
- package/src/db/schema.ts +117 -0
- package/src/server/crypto.ts +48 -0
- package/src/server/gateway-server.ts +306 -0
- package/src/server/gateway.ts +163 -0
- package/src/server/logger.ts +96 -0
- package/src/server/providers/anthropic.ts +121 -0
- package/src/server/providers/gemini.ts +112 -0
- package/src/server/providers/index.ts +20 -0
- package/src/server/providers/openai.ts +235 -0
- package/src/server/providers/types.ts +20 -0
- package/src/server/service-manager.ts +321 -0
- package/tailwind.config.js +16 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// File: /Users/pluckypan/own/AI/ai-agent-router/src/app/layout.tsx
|
|
2
|
+
import * as entry from '../../../src/app/layout.js';
|
|
3
|
+
// Check that the entry is a valid entry
|
|
4
|
+
checkFields();
|
|
5
|
+
// Check the prop type of the entry function
|
|
6
|
+
checkFields();
|
|
7
|
+
// Check the arguments and return type of the generateMetadata function
|
|
8
|
+
if ('generateMetadata' in entry) {
|
|
9
|
+
checkFields();
|
|
10
|
+
checkFields();
|
|
11
|
+
}
|
|
12
|
+
// Check the arguments and return type of the generateViewport function
|
|
13
|
+
if ('generateViewport' in entry) {
|
|
14
|
+
checkFields();
|
|
15
|
+
checkFields();
|
|
16
|
+
}
|
|
17
|
+
// Check the arguments and return type of the generateStaticParams function
|
|
18
|
+
if ('generateStaticParams' in entry) {
|
|
19
|
+
checkFields();
|
|
20
|
+
checkFields();
|
|
21
|
+
}
|
|
22
|
+
function checkFields() { }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// File: /Users/pluckypan/own/AI/ai-agent-router/src/app/logs/page.tsx
|
|
2
|
+
import * as entry from '../../../../src/app/logs/page.js';
|
|
3
|
+
// Check that the entry is a valid entry
|
|
4
|
+
checkFields();
|
|
5
|
+
// Check the prop type of the entry function
|
|
6
|
+
checkFields();
|
|
7
|
+
// Check the arguments and return type of the generateMetadata function
|
|
8
|
+
if ('generateMetadata' in entry) {
|
|
9
|
+
checkFields();
|
|
10
|
+
checkFields();
|
|
11
|
+
}
|
|
12
|
+
// Check the arguments and return type of the generateViewport function
|
|
13
|
+
if ('generateViewport' in entry) {
|
|
14
|
+
checkFields();
|
|
15
|
+
checkFields();
|
|
16
|
+
}
|
|
17
|
+
// Check the arguments and return type of the generateStaticParams function
|
|
18
|
+
if ('generateStaticParams' in entry) {
|
|
19
|
+
checkFields();
|
|
20
|
+
checkFields();
|
|
21
|
+
}
|
|
22
|
+
function checkFields() { }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// File: /Users/pluckypan/own/AI/ai-agent-router/src/app/models/page.tsx
|
|
2
|
+
import * as entry from '../../../../src/app/models/page.js';
|
|
3
|
+
// Check that the entry is a valid entry
|
|
4
|
+
checkFields();
|
|
5
|
+
// Check the prop type of the entry function
|
|
6
|
+
checkFields();
|
|
7
|
+
// Check the arguments and return type of the generateMetadata function
|
|
8
|
+
if ('generateMetadata' in entry) {
|
|
9
|
+
checkFields();
|
|
10
|
+
checkFields();
|
|
11
|
+
}
|
|
12
|
+
// Check the arguments and return type of the generateViewport function
|
|
13
|
+
if ('generateViewport' in entry) {
|
|
14
|
+
checkFields();
|
|
15
|
+
checkFields();
|
|
16
|
+
}
|
|
17
|
+
// Check the arguments and return type of the generateStaticParams function
|
|
18
|
+
if ('generateStaticParams' in entry) {
|
|
19
|
+
checkFields();
|
|
20
|
+
checkFields();
|
|
21
|
+
}
|
|
22
|
+
function checkFields() { }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// File: /Users/pluckypan/own/AI/ai-agent-router/src/app/page.tsx
|
|
2
|
+
import * as entry from '../../../src/app/page.js';
|
|
3
|
+
// Check that the entry is a valid entry
|
|
4
|
+
checkFields();
|
|
5
|
+
// Check the prop type of the entry function
|
|
6
|
+
checkFields();
|
|
7
|
+
// Check the arguments and return type of the generateMetadata function
|
|
8
|
+
if ('generateMetadata' in entry) {
|
|
9
|
+
checkFields();
|
|
10
|
+
checkFields();
|
|
11
|
+
}
|
|
12
|
+
// Check the arguments and return type of the generateViewport function
|
|
13
|
+
if ('generateViewport' in entry) {
|
|
14
|
+
checkFields();
|
|
15
|
+
checkFields();
|
|
16
|
+
}
|
|
17
|
+
// Check the arguments and return type of the generateStaticParams function
|
|
18
|
+
if ('generateStaticParams' in entry) {
|
|
19
|
+
checkFields();
|
|
20
|
+
checkFields();
|
|
21
|
+
}
|
|
22
|
+
function checkFields() { }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// File: /Users/pluckypan/own/AI/ai-agent-router/src/app/providers/page.tsx
|
|
2
|
+
import * as entry from '../../../../src/app/providers/page.js';
|
|
3
|
+
// Check that the entry is a valid entry
|
|
4
|
+
checkFields();
|
|
5
|
+
// Check the prop type of the entry function
|
|
6
|
+
checkFields();
|
|
7
|
+
// Check the arguments and return type of the generateMetadata function
|
|
8
|
+
if ('generateMetadata' in entry) {
|
|
9
|
+
checkFields();
|
|
10
|
+
checkFields();
|
|
11
|
+
}
|
|
12
|
+
// Check the arguments and return type of the generateViewport function
|
|
13
|
+
if ('generateViewport' in entry) {
|
|
14
|
+
checkFields();
|
|
15
|
+
checkFields();
|
|
16
|
+
}
|
|
17
|
+
// Check the arguments and return type of the generateStaticParams function
|
|
18
|
+
if ('generateStaticParams' in entry) {
|
|
19
|
+
checkFields();
|
|
20
|
+
checkFields();
|
|
21
|
+
}
|
|
22
|
+
function checkFields() { }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getDatabase } from '@/db/database';
|
|
3
|
+
import { getConfig, setConfig, getAllConfig } from '@/db/queries';
|
|
4
|
+
// Ensure Node.js runtime (required for SQLite)
|
|
5
|
+
export const runtime = 'nodejs';
|
|
6
|
+
export async function GET(request) {
|
|
7
|
+
try {
|
|
8
|
+
// Initialize database on request (safer than module load)
|
|
9
|
+
getDatabase();
|
|
10
|
+
const { searchParams } = new URL(request.url);
|
|
11
|
+
const key = searchParams.get('key');
|
|
12
|
+
if (key) {
|
|
13
|
+
const config = getConfig(key);
|
|
14
|
+
if (!config) {
|
|
15
|
+
return NextResponse.json({ error: 'Config not found' }, { status: 404 });
|
|
16
|
+
}
|
|
17
|
+
return NextResponse.json(config);
|
|
18
|
+
}
|
|
19
|
+
const configs = getAllConfig();
|
|
20
|
+
return NextResponse.json(configs);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('Config API error:', error);
|
|
24
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function POST(request) {
|
|
28
|
+
try {
|
|
29
|
+
// Initialize database on request
|
|
30
|
+
getDatabase();
|
|
31
|
+
const body = await request.json();
|
|
32
|
+
const { key, value } = body;
|
|
33
|
+
if (!key || value === undefined) {
|
|
34
|
+
return NextResponse.json({ error: 'Key and value are required' }, { status: 400 });
|
|
35
|
+
}
|
|
36
|
+
const config = setConfig(key, typeof value === 'string' ? value : JSON.stringify(value));
|
|
37
|
+
return NextResponse.json(config);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error('Config POST API error:', error);
|
|
41
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getDatabase } from '@/db/database';
|
|
3
|
+
import { handleGatewayRequest } from '@/server/gateway';
|
|
4
|
+
// Ensure Node.js runtime (required for SQLite)
|
|
5
|
+
export const runtime = 'nodejs';
|
|
6
|
+
export async function POST(request, { params }) {
|
|
7
|
+
return handleRequest(request, params, 'POST');
|
|
8
|
+
}
|
|
9
|
+
export async function GET(request, { params }) {
|
|
10
|
+
return handleRequest(request, params, 'GET');
|
|
11
|
+
}
|
|
12
|
+
export async function PUT(request, { params }) {
|
|
13
|
+
return handleRequest(request, params, 'PUT');
|
|
14
|
+
}
|
|
15
|
+
export async function PATCH(request, { params }) {
|
|
16
|
+
return handleRequest(request, params, 'PATCH');
|
|
17
|
+
}
|
|
18
|
+
export async function DELETE(request, { params }) {
|
|
19
|
+
return handleRequest(request, params, 'DELETE');
|
|
20
|
+
}
|
|
21
|
+
async function handleRequest(request, params, method) {
|
|
22
|
+
try {
|
|
23
|
+
getDatabase();
|
|
24
|
+
// Extract model ID from path or query
|
|
25
|
+
// Common patterns: /v1/models/{model_id}/... or ?model=...
|
|
26
|
+
const path = params.path.join('/');
|
|
27
|
+
const searchParams = request.nextUrl.searchParams;
|
|
28
|
+
// Try to get model from query or path
|
|
29
|
+
let modelId = searchParams.get('model') || searchParams.get('model_id');
|
|
30
|
+
// If not in query, try to extract from path (e.g., /v1/models/gpt-4/completions)
|
|
31
|
+
if (!modelId) {
|
|
32
|
+
const pathParts = path.split('/');
|
|
33
|
+
const modelsIndex = pathParts.indexOf('models');
|
|
34
|
+
if (modelsIndex >= 0 && pathParts[modelsIndex + 1]) {
|
|
35
|
+
modelId = pathParts[modelsIndex + 1];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// If still no model, try to get from body
|
|
39
|
+
if (!modelId) {
|
|
40
|
+
try {
|
|
41
|
+
const body = await request.json();
|
|
42
|
+
modelId = body.model || body.model_id;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Body might not be JSON or might be empty
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!modelId) {
|
|
49
|
+
return NextResponse.json({ error: { message: 'Model ID not specified' } }, { status: 400 });
|
|
50
|
+
}
|
|
51
|
+
// Get request body
|
|
52
|
+
let body = null;
|
|
53
|
+
try {
|
|
54
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
55
|
+
const text = await request.text();
|
|
56
|
+
if (text) {
|
|
57
|
+
body = JSON.parse(text);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Body might not be JSON
|
|
63
|
+
}
|
|
64
|
+
// Build gateway request
|
|
65
|
+
const gatewayRequest = {
|
|
66
|
+
method,
|
|
67
|
+
path: '/' + path,
|
|
68
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
69
|
+
query: Object.fromEntries(searchParams.entries()),
|
|
70
|
+
body,
|
|
71
|
+
};
|
|
72
|
+
// Handle the request
|
|
73
|
+
const response = await handleGatewayRequest(modelId, gatewayRequest);
|
|
74
|
+
// Return response
|
|
75
|
+
return NextResponse.json(response.body, {
|
|
76
|
+
status: response.status,
|
|
77
|
+
headers: response.headers,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return NextResponse.json({ error: { message: error.message || 'Internal server error' } }, { status: 500 });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getDatabase } from '@/db/database';
|
|
3
|
+
import { handleGatewayRequest } from '@/server/gateway';
|
|
4
|
+
// Ensure Node.js runtime (required for SQLite)
|
|
5
|
+
export const runtime = 'nodejs';
|
|
6
|
+
// Handle requests to /api/gateway (root gateway endpoint)
|
|
7
|
+
export async function POST(request) {
|
|
8
|
+
return handleGatewayRequestDirect(request, 'POST');
|
|
9
|
+
}
|
|
10
|
+
export async function GET(request) {
|
|
11
|
+
return handleGatewayRequestDirect(request, 'GET');
|
|
12
|
+
}
|
|
13
|
+
async function handleGatewayRequestDirect(request, method) {
|
|
14
|
+
try {
|
|
15
|
+
getDatabase();
|
|
16
|
+
const searchParams = request.nextUrl.searchParams;
|
|
17
|
+
let modelId = searchParams.get('model') || searchParams.get('model_id');
|
|
18
|
+
// Try to get from body
|
|
19
|
+
if (!modelId) {
|
|
20
|
+
try {
|
|
21
|
+
const body = await request.json();
|
|
22
|
+
modelId = body.model || body.model_id;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Body might not be JSON
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!modelId) {
|
|
29
|
+
return NextResponse.json({ error: { message: 'Model ID not specified' } }, { status: 400 });
|
|
30
|
+
}
|
|
31
|
+
// Get request body
|
|
32
|
+
let body = null;
|
|
33
|
+
try {
|
|
34
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
35
|
+
const text = await request.text();
|
|
36
|
+
if (text) {
|
|
37
|
+
body = JSON.parse(text);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Body might not be JSON
|
|
43
|
+
}
|
|
44
|
+
// Build gateway request
|
|
45
|
+
const gatewayRequest = {
|
|
46
|
+
method,
|
|
47
|
+
path: '/',
|
|
48
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
49
|
+
query: Object.fromEntries(searchParams.entries()),
|
|
50
|
+
body,
|
|
51
|
+
};
|
|
52
|
+
// Handle the request
|
|
53
|
+
const response = await handleGatewayRequest(modelId, gatewayRequest);
|
|
54
|
+
// Return response
|
|
55
|
+
return NextResponse.json(response.body, {
|
|
56
|
+
status: response.status,
|
|
57
|
+
headers: response.headers,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return NextResponse.json({ error: { message: error.message || 'Internal server error' } }, { status: 500 });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getDatabase } from '@/db/database';
|
|
3
|
+
import { getRequestLogs, getRequestLogById, getRequestLogCount } from '@/db/queries';
|
|
4
|
+
// Ensure Node.js runtime (required for SQLite)
|
|
5
|
+
export const runtime = 'nodejs';
|
|
6
|
+
export async function GET(request) {
|
|
7
|
+
try {
|
|
8
|
+
getDatabase();
|
|
9
|
+
const { searchParams } = new URL(request.url);
|
|
10
|
+
const id = searchParams.get('id');
|
|
11
|
+
const limit = parseInt(searchParams.get('limit') || '100');
|
|
12
|
+
const offset = parseInt(searchParams.get('offset') || '0');
|
|
13
|
+
const modelId = searchParams.get('model_id');
|
|
14
|
+
if (id) {
|
|
15
|
+
const log = getRequestLogById(parseInt(id));
|
|
16
|
+
if (!log) {
|
|
17
|
+
return NextResponse.json({ error: 'Log not found' }, { status: 404 });
|
|
18
|
+
}
|
|
19
|
+
return NextResponse.json(log);
|
|
20
|
+
}
|
|
21
|
+
const logs = getRequestLogs(limit, offset, modelId ? parseInt(modelId) : undefined);
|
|
22
|
+
const total = getRequestLogCount(modelId ? parseInt(modelId) : undefined);
|
|
23
|
+
return NextResponse.json({
|
|
24
|
+
logs,
|
|
25
|
+
total,
|
|
26
|
+
limit,
|
|
27
|
+
offset,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('Logs API error:', error);
|
|
32
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getDatabase } from '@/db/database';
|
|
3
|
+
import { getAllModels, createModel, updateModel, deleteModel, getModelsByProvider, getProviderById, } from '@/db/queries';
|
|
4
|
+
import { getProviderAdapter } from '@/server/providers';
|
|
5
|
+
// Ensure Node.js runtime (required for SQLite)
|
|
6
|
+
export const runtime = 'nodejs';
|
|
7
|
+
export async function GET(request) {
|
|
8
|
+
try {
|
|
9
|
+
getDatabase();
|
|
10
|
+
const { searchParams } = new URL(request.url);
|
|
11
|
+
const providerId = searchParams.get('provider_id');
|
|
12
|
+
if (providerId) {
|
|
13
|
+
const models = getModelsByProvider(parseInt(providerId));
|
|
14
|
+
return NextResponse.json(models);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const models = getAllModels();
|
|
18
|
+
return NextResponse.json(models);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error('Models API error:', error);
|
|
23
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function POST(request) {
|
|
27
|
+
try {
|
|
28
|
+
getDatabase();
|
|
29
|
+
const body = await request.json();
|
|
30
|
+
const { provider_id, name, model_id, enabled } = body;
|
|
31
|
+
if (!provider_id || !name || !model_id) {
|
|
32
|
+
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
|
33
|
+
}
|
|
34
|
+
const model = createModel({
|
|
35
|
+
provider_id,
|
|
36
|
+
name,
|
|
37
|
+
model_id,
|
|
38
|
+
enabled: enabled !== undefined ? enabled : true,
|
|
39
|
+
});
|
|
40
|
+
return NextResponse.json(model);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error('Models API error:', error);
|
|
44
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function PUT(request) {
|
|
48
|
+
try {
|
|
49
|
+
getDatabase();
|
|
50
|
+
const body = await request.json();
|
|
51
|
+
const { id, name, model_id, enabled, provider_id } = body;
|
|
52
|
+
if (!id) {
|
|
53
|
+
return NextResponse.json({ error: 'Model ID is required' }, { status: 400 });
|
|
54
|
+
}
|
|
55
|
+
const updateData = {};
|
|
56
|
+
if (name !== undefined)
|
|
57
|
+
updateData.name = name;
|
|
58
|
+
if (model_id !== undefined)
|
|
59
|
+
updateData.model_id = model_id;
|
|
60
|
+
if (enabled !== undefined)
|
|
61
|
+
updateData.enabled = enabled;
|
|
62
|
+
if (provider_id !== undefined)
|
|
63
|
+
updateData.provider_id = provider_id;
|
|
64
|
+
const model = updateModel(id, updateData);
|
|
65
|
+
if (!model) {
|
|
66
|
+
return NextResponse.json({ error: 'Model not found' }, { status: 404 });
|
|
67
|
+
}
|
|
68
|
+
return NextResponse.json(model);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error('Models API error:', error);
|
|
72
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export async function DELETE(request) {
|
|
76
|
+
try {
|
|
77
|
+
getDatabase();
|
|
78
|
+
const { searchParams } = new URL(request.url);
|
|
79
|
+
const id = searchParams.get('id');
|
|
80
|
+
if (!id) {
|
|
81
|
+
return NextResponse.json({ error: 'Model ID is required' }, { status: 400 });
|
|
82
|
+
}
|
|
83
|
+
const success = deleteModel(parseInt(id));
|
|
84
|
+
if (!success) {
|
|
85
|
+
return NextResponse.json({ error: 'Model not found' }, { status: 404 });
|
|
86
|
+
}
|
|
87
|
+
return NextResponse.json({ success: true });
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
console.error('Models API error:', error);
|
|
91
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Fetch models from provider
|
|
95
|
+
export async function PATCH(request) {
|
|
96
|
+
try {
|
|
97
|
+
getDatabase();
|
|
98
|
+
const body = await request.json();
|
|
99
|
+
const { provider_id } = body;
|
|
100
|
+
if (!provider_id) {
|
|
101
|
+
return NextResponse.json({ error: 'Provider ID is required' }, { status: 400 });
|
|
102
|
+
}
|
|
103
|
+
const provider = getProviderById(provider_id);
|
|
104
|
+
if (!provider) {
|
|
105
|
+
return NextResponse.json({ error: 'Provider not found' }, { status: 404 });
|
|
106
|
+
}
|
|
107
|
+
console.log('[Models API] Fetching models for provider:', {
|
|
108
|
+
providerId: provider_id,
|
|
109
|
+
providerName: provider.name,
|
|
110
|
+
protocol: provider.protocol,
|
|
111
|
+
baseUrl: provider.base_url,
|
|
112
|
+
});
|
|
113
|
+
const adapter = getProviderAdapter(provider.protocol);
|
|
114
|
+
const models = await adapter.listModels(provider);
|
|
115
|
+
console.log('[Models API] Fetched models:', models.length);
|
|
116
|
+
// Create or update models
|
|
117
|
+
const results = [];
|
|
118
|
+
for (const modelInfo of models) {
|
|
119
|
+
try {
|
|
120
|
+
const existing = getAllModels().find(m => m.provider_id === provider_id && m.model_id === modelInfo.id);
|
|
121
|
+
if (existing) {
|
|
122
|
+
updateModel(existing.id, {
|
|
123
|
+
name: modelInfo.name,
|
|
124
|
+
model_id: modelInfo.id,
|
|
125
|
+
});
|
|
126
|
+
results.push({ ...existing, name: modelInfo.name });
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const newModel = createModel({
|
|
130
|
+
provider_id,
|
|
131
|
+
name: modelInfo.name,
|
|
132
|
+
model_id: modelInfo.id,
|
|
133
|
+
enabled: false, // 默认关闭
|
|
134
|
+
});
|
|
135
|
+
results.push(newModel);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error(`Failed to create/update model ${modelInfo.id}:`, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return NextResponse.json({
|
|
143
|
+
success: true,
|
|
144
|
+
models: results,
|
|
145
|
+
count: results.length,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('Models API error:', error);
|
|
150
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getDatabase } from '@/db/database';
|
|
3
|
+
import { getAllProviders, getProviderById, createProvider, updateProvider, deleteProvider } from '@/db/queries';
|
|
4
|
+
import { encryptApiKey, decryptApiKey } from '@/server/crypto';
|
|
5
|
+
// Ensure Node.js runtime (required for SQLite)
|
|
6
|
+
export const runtime = 'nodejs';
|
|
7
|
+
export async function GET(request) {
|
|
8
|
+
try {
|
|
9
|
+
getDatabase();
|
|
10
|
+
const { searchParams } = new URL(request.url);
|
|
11
|
+
const id = searchParams.get('id');
|
|
12
|
+
const includeKey = searchParams.get('includeKey') === 'true';
|
|
13
|
+
// If ID is provided, return single provider (for editing)
|
|
14
|
+
if (id) {
|
|
15
|
+
const provider = getProviderById(parseInt(id));
|
|
16
|
+
if (!provider) {
|
|
17
|
+
return NextResponse.json({ error: 'Provider not found' }, { status: 404 });
|
|
18
|
+
}
|
|
19
|
+
// Return with decrypted API key if requested (for editing)
|
|
20
|
+
return NextResponse.json({
|
|
21
|
+
...provider,
|
|
22
|
+
api_key: includeKey ? decryptApiKey(provider.api_key) : '***',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// Return all providers
|
|
26
|
+
const providers = getAllProviders();
|
|
27
|
+
// Don't return API keys in list view
|
|
28
|
+
const sanitized = providers.map(p => ({
|
|
29
|
+
...p,
|
|
30
|
+
api_key: '***',
|
|
31
|
+
}));
|
|
32
|
+
return NextResponse.json(sanitized);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error('Providers API error:', error);
|
|
36
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function POST(request) {
|
|
40
|
+
try {
|
|
41
|
+
getDatabase();
|
|
42
|
+
const body = await request.json();
|
|
43
|
+
const { name, protocol, base_url, api_key } = body;
|
|
44
|
+
if (!name || !protocol || !base_url || !api_key) {
|
|
45
|
+
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
|
46
|
+
}
|
|
47
|
+
const encryptedKey = encryptApiKey(api_key.trim());
|
|
48
|
+
const provider = createProvider({
|
|
49
|
+
name,
|
|
50
|
+
protocol,
|
|
51
|
+
base_url,
|
|
52
|
+
api_key: encryptedKey,
|
|
53
|
+
});
|
|
54
|
+
return NextResponse.json({
|
|
55
|
+
...provider,
|
|
56
|
+
api_key: '***',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error('Providers API error:', error);
|
|
61
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function PUT(request) {
|
|
65
|
+
try {
|
|
66
|
+
getDatabase();
|
|
67
|
+
const body = await request.json();
|
|
68
|
+
const { id, name, protocol, base_url, api_key } = body;
|
|
69
|
+
if (!id) {
|
|
70
|
+
return NextResponse.json({ error: 'Provider ID is required' }, { status: 400 });
|
|
71
|
+
}
|
|
72
|
+
const existingProvider = getProviderById(id);
|
|
73
|
+
if (!existingProvider) {
|
|
74
|
+
return NextResponse.json({ error: 'Provider not found' }, { status: 404 });
|
|
75
|
+
}
|
|
76
|
+
const updateData = {};
|
|
77
|
+
if (name !== undefined)
|
|
78
|
+
updateData.name = name;
|
|
79
|
+
if (protocol !== undefined)
|
|
80
|
+
updateData.protocol = protocol;
|
|
81
|
+
if (base_url !== undefined)
|
|
82
|
+
updateData.base_url = base_url;
|
|
83
|
+
if (api_key !== undefined && api_key !== null && api_key.trim() !== '') {
|
|
84
|
+
updateData.api_key = encryptApiKey(api_key.trim());
|
|
85
|
+
}
|
|
86
|
+
const provider = updateProvider(id, updateData);
|
|
87
|
+
if (!provider) {
|
|
88
|
+
return NextResponse.json({ error: 'Provider not found' }, { status: 404 });
|
|
89
|
+
}
|
|
90
|
+
return NextResponse.json({
|
|
91
|
+
...provider,
|
|
92
|
+
api_key: '***',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error('Providers API error:', error);
|
|
97
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export async function DELETE(request) {
|
|
101
|
+
try {
|
|
102
|
+
getDatabase();
|
|
103
|
+
const { searchParams } = new URL(request.url);
|
|
104
|
+
const id = searchParams.get('id');
|
|
105
|
+
if (!id) {
|
|
106
|
+
return NextResponse.json({ error: 'Provider ID is required' }, { status: 400 });
|
|
107
|
+
}
|
|
108
|
+
const success = deleteProvider(parseInt(id));
|
|
109
|
+
if (!success) {
|
|
110
|
+
return NextResponse.json({ error: 'Provider not found' }, { status: 404 });
|
|
111
|
+
}
|
|
112
|
+
return NextResponse.json({ success: true });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error('Providers API error:', error);
|
|
116
|
+
return NextResponse.json({ error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }, { status: 500 });
|
|
117
|
+
}
|
|
118
|
+
}
|