nextjs-chatbot-ui 1.4.0 → 1.5.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 +145 -5
- package/api/chat.ts +97 -0
- package/api/database/collections.ts +80 -0
- package/api/database/columns.ts +80 -0
- package/api/database/process-embeddings.ts +154 -0
- package/api/database/test.ts +67 -0
- package/hooks/useAdminSetup.ts +86 -0
- package/hooks/useChatbot.ts +27 -0
- package/index.tsx +9 -5
- package/package.json +27 -5
- package/scripts/setup.js +550 -0
package/README.md
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nextjs-chatbot-ui
|
|
2
2
|
|
|
3
|
-
A beautiful, configurable chatbot UI component for Next.js
|
|
3
|
+
A beautiful, configurable chatbot UI component for Next.js with Tailwind CSS. Perfect for customer support, AI assistants, and chat applications.
|
|
4
|
+
|
|
5
|
+
## ⚡ Super Simple Usage (Just 2 Files!)
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
// app/page.tsx
|
|
9
|
+
'use client';
|
|
10
|
+
import { Chatbot } from 'nextjs-chatbot-ui';
|
|
11
|
+
export default function Home() {
|
|
12
|
+
return <Chatbot config={{ backendUrl: '/api/chat' }} />;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
// app/api/chat/route.ts
|
|
18
|
+
export async function POST(req: Request) {
|
|
19
|
+
const { message } = await req.json();
|
|
20
|
+
return Response.json({ message: `You said: ${message}` });
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Done!** See `SIMPLE_SETUP.md` for more examples.
|
|
4
25
|
|
|
5
26
|
## Features
|
|
6
27
|
|
|
@@ -33,6 +54,81 @@ This package requires:
|
|
|
33
54
|
npm install nextjs-chatbot-ui
|
|
34
55
|
```
|
|
35
56
|
|
|
57
|
+
## 🚀 Super Simple Setup (Complete RAG Chatbot in 3 Steps!)
|
|
58
|
+
|
|
59
|
+
**Everything works automatically!** Just run the setup script and add your `.env` file.
|
|
60
|
+
|
|
61
|
+
### Step 1: Run Setup Script
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx nextjs-chatbot-ui-setup
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This automatically creates all API routes needed for the chatbot and admin setup.
|
|
68
|
+
|
|
69
|
+
### Step 2: Install Dependencies & Add Environment Variables
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install mongodb pg chromadb openai
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Create `.env.local` file:
|
|
76
|
+
```env
|
|
77
|
+
OPENAI_API_KEY=your_openai_api_key_here
|
|
78
|
+
CHROMADB_URL=http://localhost:8000
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 3: Use Components
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// app/page.tsx
|
|
85
|
+
'use client';
|
|
86
|
+
import { Chatbot, AdminSetup } from 'nextjs-chatbot-ui';
|
|
87
|
+
|
|
88
|
+
export default function Home() {
|
|
89
|
+
return (
|
|
90
|
+
<div>
|
|
91
|
+
<AdminSetup />
|
|
92
|
+
<Chatbot config={{ backendUrl: '/api/chat' }} />
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**That's it!** 🎉
|
|
99
|
+
|
|
100
|
+
- Use `<AdminSetup />` to connect to your database and process embeddings
|
|
101
|
+
- Use `<Chatbot />` to ask questions about your data
|
|
102
|
+
- Everything else is handled automatically!
|
|
103
|
+
|
|
104
|
+
## Quick Start (Minimal Code)
|
|
105
|
+
|
|
106
|
+
### Simplest Usage (Just 2 files!)
|
|
107
|
+
|
|
108
|
+
**1. Add to your page:**
|
|
109
|
+
```javascript
|
|
110
|
+
// app/page.tsx
|
|
111
|
+
'use client';
|
|
112
|
+
import { Chatbot } from 'nextjs-chatbot-ui';
|
|
113
|
+
|
|
114
|
+
export default function Home() {
|
|
115
|
+
return <Chatbot config={{ backendUrl: '/api/chat' }} />;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**2. Create API endpoint:**
|
|
120
|
+
```javascript
|
|
121
|
+
// app/api/chat/route.ts
|
|
122
|
+
import { NextResponse } from 'next/server';
|
|
123
|
+
|
|
124
|
+
export async function POST(req: Request) {
|
|
125
|
+
const { message } = await req.json();
|
|
126
|
+
return NextResponse.json({ message: `You said: ${message}` });
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Done!** See `SIMPLE_SETUP.md` for more examples.
|
|
131
|
+
|
|
36
132
|
## Usage
|
|
37
133
|
|
|
38
134
|
### Basic Setup
|
|
@@ -230,7 +326,9 @@ interface ChatbotConfig {
|
|
|
230
326
|
|
|
231
327
|
### Backend API Requirements
|
|
232
328
|
|
|
233
|
-
|
|
329
|
+
**✅ Works with ANY backend!** Express, FastAPI, Flask, Django, NestJS, Go, or any other framework.
|
|
330
|
+
|
|
331
|
+
Your backend just needs to implement **one POST endpoint** that accepts and returns JSON:
|
|
234
332
|
|
|
235
333
|
**Request:**
|
|
236
334
|
```json
|
|
@@ -243,7 +341,7 @@ Your backend should accept POST requests with the following format:
|
|
|
243
341
|
}
|
|
244
342
|
```
|
|
245
343
|
|
|
246
|
-
**Response:**
|
|
344
|
+
**Response (either format works):**
|
|
247
345
|
```json
|
|
248
346
|
{
|
|
249
347
|
"message": "Bot's response text",
|
|
@@ -260,7 +358,10 @@ or
|
|
|
260
358
|
}
|
|
261
359
|
```
|
|
262
360
|
|
|
263
|
-
**Note:**
|
|
361
|
+
**Note:**
|
|
362
|
+
- The `suggestedReplies` or `suggestions` field is optional
|
|
363
|
+
- The component accepts either `message` or `response` in the response
|
|
364
|
+
- See `BACKEND_INTEGRATION.md` for examples with different backend frameworks (Express, FastAPI, Flask, Django, NestJS, Go, etc.)
|
|
264
365
|
|
|
265
366
|
### Example Implementation
|
|
266
367
|
|
|
@@ -500,6 +601,45 @@ If the database connection is not establishing:
|
|
|
500
601
|
|
|
501
602
|
5. **Verify database credentials**: Ensure your database host, port, username, password, and database name are correct.
|
|
502
603
|
|
|
604
|
+
## RAG Chatbot Architecture
|
|
605
|
+
|
|
606
|
+
This package supports RAG (Retrieval-Augmented Generation) chatbot architecture for answering questions about your database.
|
|
607
|
+
|
|
608
|
+
### Architecture Flow
|
|
609
|
+
|
|
610
|
+
```
|
|
611
|
+
React UI → Express API → MongoDB + ChromaDB → OpenAI
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
1. **Setup Phase**: Use `AdminSetup` component to connect to database, select collections/columns, and process embeddings
|
|
615
|
+
2. **Query Phase**: Use `Chatbot` component to ask questions, which are answered using vector search and LLM generation
|
|
616
|
+
|
|
617
|
+
### Quick Start with RAG
|
|
618
|
+
|
|
619
|
+
1. **Set up backend** (see `server.js` or `example-nextjs-api-routes.md`):
|
|
620
|
+
- Express server with MongoDB, ChromaDB, and OpenAI integration
|
|
621
|
+
- Endpoints: `/api/search`, `/api/database/process-embeddings`
|
|
622
|
+
|
|
623
|
+
2. **Configure chatbot**:
|
|
624
|
+
```javascript
|
|
625
|
+
const config: ChatbotConfig = {
|
|
626
|
+
backendUrl: '/api/search', // RAG search endpoint
|
|
627
|
+
labels: {
|
|
628
|
+
title: 'RAG Chatbot',
|
|
629
|
+
placeholder: 'Ask me anything about your data...',
|
|
630
|
+
},
|
|
631
|
+
};
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
3. **Use AdminSetup** to:
|
|
635
|
+
- Connect to your database
|
|
636
|
+
- Select collections and columns
|
|
637
|
+
- Process embeddings (fetches data → converts to embeddings → stores in ChromaDB)
|
|
638
|
+
|
|
639
|
+
4. **Query your data** using the Chatbot component
|
|
640
|
+
|
|
641
|
+
For complete RAG architecture documentation, see `RAG_ARCHITECTURE.md`.
|
|
642
|
+
|
|
503
643
|
## License
|
|
504
644
|
|
|
505
645
|
MIT
|
package/api/chat.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { ChromaClient } from 'chromadb';
|
|
3
|
+
import OpenAI from 'openai';
|
|
4
|
+
|
|
5
|
+
const openai = new OpenAI({
|
|
6
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const chromaClient = new ChromaClient({
|
|
10
|
+
path: process.env.CHROMADB_URL || 'http://localhost:8000',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export async function POST(request: NextRequest) {
|
|
14
|
+
try {
|
|
15
|
+
const { message, userInfo } = await request.json();
|
|
16
|
+
|
|
17
|
+
if (!message) {
|
|
18
|
+
return NextResponse.json(
|
|
19
|
+
{ error: 'Message is required' },
|
|
20
|
+
{ status: 400 }
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
25
|
+
return NextResponse.json(
|
|
26
|
+
{ error: 'OPENAI_API_KEY is not set in environment variables' },
|
|
27
|
+
{ status: 500 }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Get query embedding
|
|
32
|
+
const queryEmbedding = await openai.embeddings.create({
|
|
33
|
+
model: 'text-embedding-3-small',
|
|
34
|
+
input: message,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Search in ChromaDB
|
|
38
|
+
const collectionName = process.env.CHROMA_COLLECTION_NAME || 'db_default';
|
|
39
|
+
let chromaCollection;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
chromaCollection = await chromaClient.getCollection({
|
|
43
|
+
name: collectionName,
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
// If collection doesn't exist, return a helpful message
|
|
47
|
+
return NextResponse.json({
|
|
48
|
+
message: 'Vector database not initialized. Please use AdminSetup to process embeddings first.',
|
|
49
|
+
response: 'Vector database not initialized. Please use AdminSetup to process embeddings first.',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const results = await chromaCollection.query({
|
|
54
|
+
queryEmbeddings: [queryEmbedding.data[0].embedding],
|
|
55
|
+
nResults: 5,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Extract context
|
|
59
|
+
const contexts = results.documents[0] || [];
|
|
60
|
+
const contextText = contexts.length > 0
|
|
61
|
+
? contexts.join('\n\n')
|
|
62
|
+
: 'No relevant context found.';
|
|
63
|
+
|
|
64
|
+
// Generate response with OpenAI
|
|
65
|
+
const completion = await openai.chat.completions.create({
|
|
66
|
+
model: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
|
|
67
|
+
messages: [
|
|
68
|
+
{
|
|
69
|
+
role: 'system',
|
|
70
|
+
content: 'You are a helpful assistant that answers questions based on the provided context. If the context does not contain relevant information, say so politely.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
role: 'user',
|
|
74
|
+
content: `Context:\n${contextText}\n\nQuestion: ${message}\n\nAnswer:`,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
temperature: 0.7,
|
|
78
|
+
max_tokens: 500,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const answer = completion.choices[0].message.content;
|
|
82
|
+
|
|
83
|
+
return NextResponse.json({
|
|
84
|
+
message: answer,
|
|
85
|
+
response: answer,
|
|
86
|
+
});
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
console.error('Chat error:', error);
|
|
89
|
+
return NextResponse.json(
|
|
90
|
+
{
|
|
91
|
+
message: error.message || 'Failed to process message',
|
|
92
|
+
response: error.message || 'Failed to process message',
|
|
93
|
+
},
|
|
94
|
+
{ status: 500 }
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { MongoClient } from 'mongodb';
|
|
3
|
+
import { Client } from 'pg';
|
|
4
|
+
|
|
5
|
+
export async function POST(request: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
const { databaseType, connection } = await request.json();
|
|
8
|
+
|
|
9
|
+
if (!databaseType || !connection) {
|
|
10
|
+
return NextResponse.json(
|
|
11
|
+
{ collections: [], tables: [] },
|
|
12
|
+
{ status: 400 }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (databaseType === 'mongodb') {
|
|
17
|
+
try {
|
|
18
|
+
const uri = connection.connectionString ||
|
|
19
|
+
`mongodb://${connection.username ? `${connection.username}:${connection.password}@` : ''}${connection.host}:${connection.port || 27017}/${connection.database}`;
|
|
20
|
+
|
|
21
|
+
const client = new MongoClient(uri);
|
|
22
|
+
await client.connect();
|
|
23
|
+
const db = client.db(connection.database);
|
|
24
|
+
const collections = await db.listCollections().toArray();
|
|
25
|
+
await client.close();
|
|
26
|
+
|
|
27
|
+
return NextResponse.json({
|
|
28
|
+
collections: collections.map(c => c.name),
|
|
29
|
+
tables: collections.map(c => c.name),
|
|
30
|
+
});
|
|
31
|
+
} catch (error: any) {
|
|
32
|
+
return NextResponse.json(
|
|
33
|
+
{ collections: [], tables: [], error: error.message },
|
|
34
|
+
{ status: 500 }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
} else if (databaseType === 'postgresql') {
|
|
38
|
+
try {
|
|
39
|
+
const client = new Client({
|
|
40
|
+
host: connection.host,
|
|
41
|
+
port: connection.port || 5432,
|
|
42
|
+
database: connection.database,
|
|
43
|
+
user: connection.username,
|
|
44
|
+
password: connection.password,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await client.connect();
|
|
48
|
+
const result = await client.query(`
|
|
49
|
+
SELECT table_name
|
|
50
|
+
FROM information_schema.tables
|
|
51
|
+
WHERE table_schema = 'public'
|
|
52
|
+
`);
|
|
53
|
+
await client.end();
|
|
54
|
+
|
|
55
|
+
const tables = result.rows.map(row => row.table_name);
|
|
56
|
+
|
|
57
|
+
return NextResponse.json({
|
|
58
|
+
collections: tables,
|
|
59
|
+
tables: tables,
|
|
60
|
+
});
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ collections: [], tables: [], error: error.message },
|
|
64
|
+
{ status: 500 }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ collections: [], tables: [] },
|
|
70
|
+
{ status: 400 }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
} catch (error: any) {
|
|
74
|
+
console.error('Fetch collections error:', error);
|
|
75
|
+
return NextResponse.json(
|
|
76
|
+
{ collections: [], tables: [], error: error.message },
|
|
77
|
+
{ status: 500 }
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { MongoClient } from 'mongodb';
|
|
3
|
+
import { Client } from 'pg';
|
|
4
|
+
|
|
5
|
+
export async function POST(request: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
const { databaseType, connection, collection } = await request.json();
|
|
8
|
+
|
|
9
|
+
if (!databaseType || !connection || !collection) {
|
|
10
|
+
return NextResponse.json(
|
|
11
|
+
{ columns: [] },
|
|
12
|
+
{ status: 400 }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (databaseType === 'mongodb') {
|
|
17
|
+
try {
|
|
18
|
+
const uri = connection.connectionString ||
|
|
19
|
+
`mongodb://${connection.username ? `${connection.username}:${connection.password}@` : ''}${connection.host}:${connection.port || 27017}/${connection.database}`;
|
|
20
|
+
|
|
21
|
+
const client = new MongoClient(uri);
|
|
22
|
+
await client.connect();
|
|
23
|
+
const db = client.db(connection.database);
|
|
24
|
+
const mongoCollection = db.collection(collection);
|
|
25
|
+
|
|
26
|
+
// Get a sample document to extract keys
|
|
27
|
+
const sampleDoc = await mongoCollection.findOne({});
|
|
28
|
+
await client.close();
|
|
29
|
+
|
|
30
|
+
const columns = sampleDoc ? Object.keys(sampleDoc).filter(key => key !== '_id') : [];
|
|
31
|
+
|
|
32
|
+
return NextResponse.json({ columns });
|
|
33
|
+
} catch (error: any) {
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ columns: [], error: error.message },
|
|
36
|
+
{ status: 500 }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
} else if (databaseType === 'postgresql') {
|
|
40
|
+
try {
|
|
41
|
+
const client = new Client({
|
|
42
|
+
host: connection.host,
|
|
43
|
+
port: connection.port || 5432,
|
|
44
|
+
database: connection.database,
|
|
45
|
+
user: connection.username,
|
|
46
|
+
password: connection.password,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await client.connect();
|
|
50
|
+
const result = await client.query(`
|
|
51
|
+
SELECT column_name
|
|
52
|
+
FROM information_schema.columns
|
|
53
|
+
WHERE table_schema = 'public'
|
|
54
|
+
AND table_name = $1
|
|
55
|
+
`, [collection]);
|
|
56
|
+
await client.end();
|
|
57
|
+
|
|
58
|
+
const columns = result.rows.map(row => row.column_name);
|
|
59
|
+
|
|
60
|
+
return NextResponse.json({ columns });
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ columns: [], error: error.message },
|
|
64
|
+
{ status: 500 }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ columns: [] },
|
|
70
|
+
{ status: 400 }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
} catch (error: any) {
|
|
74
|
+
console.error('Fetch columns error:', error);
|
|
75
|
+
return NextResponse.json(
|
|
76
|
+
{ columns: [], error: error.message },
|
|
77
|
+
{ status: 500 }
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { MongoClient } from 'mongodb';
|
|
3
|
+
import { Client } from 'pg';
|
|
4
|
+
import { ChromaClient } from 'chromadb';
|
|
5
|
+
import OpenAI from 'openai';
|
|
6
|
+
|
|
7
|
+
const openai = new OpenAI({
|
|
8
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const chromaClient = new ChromaClient({
|
|
12
|
+
path: process.env.CHROMADB_URL || 'http://localhost:8000',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export async function POST(request: NextRequest) {
|
|
16
|
+
try {
|
|
17
|
+
const { connection, collections, columnSelection } = await request.json();
|
|
18
|
+
|
|
19
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ success: false, message: 'OPENAI_API_KEY is not set in environment variables' },
|
|
22
|
+
{ status: 500 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!connection || !collections || !Array.isArray(collections) || collections.length === 0) {
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ success: false, message: 'Connection and collections are required' },
|
|
29
|
+
{ status: 400 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const databaseType = connection.type || 'mongodb';
|
|
34
|
+
const dbName = connection.database;
|
|
35
|
+
|
|
36
|
+
// Initialize database connection
|
|
37
|
+
let db: any;
|
|
38
|
+
let client: any;
|
|
39
|
+
|
|
40
|
+
if (databaseType === 'mongodb') {
|
|
41
|
+
const uri = connection.connectionString ||
|
|
42
|
+
`mongodb://${connection.username ? `${connection.username}:${connection.password}@` : ''}${connection.host}:${connection.port || 27017}/${dbName}`;
|
|
43
|
+
client = new MongoClient(uri);
|
|
44
|
+
await client.connect();
|
|
45
|
+
db = client.db(dbName);
|
|
46
|
+
} else if (databaseType === 'postgresql' || databaseType === 'postgres') {
|
|
47
|
+
client = new Client({
|
|
48
|
+
host: connection.host,
|
|
49
|
+
port: connection.port || 5432,
|
|
50
|
+
database: dbName,
|
|
51
|
+
user: connection.username,
|
|
52
|
+
password: connection.password,
|
|
53
|
+
});
|
|
54
|
+
await client.connect();
|
|
55
|
+
} else {
|
|
56
|
+
return NextResponse.json(
|
|
57
|
+
{ success: false, message: 'Unsupported database type' },
|
|
58
|
+
{ status: 400 }
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Get or create ChromaDB collection
|
|
63
|
+
const collectionName = process.env.CHROMA_COLLECTION_NAME || `db_${dbName}`;
|
|
64
|
+
let chromaCollection;
|
|
65
|
+
try {
|
|
66
|
+
chromaCollection = await chromaClient.getOrCreateCollection({
|
|
67
|
+
name: collectionName,
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
chromaCollection = await chromaClient.createCollection({
|
|
71
|
+
name: collectionName,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let totalProcessed = 0;
|
|
76
|
+
|
|
77
|
+
// Process each collection
|
|
78
|
+
for (const collConfig of collections) {
|
|
79
|
+
const { collection: collName, columns } = collConfig;
|
|
80
|
+
|
|
81
|
+
if (!columns || columns.length === 0) continue;
|
|
82
|
+
|
|
83
|
+
let documents: any[] = [];
|
|
84
|
+
|
|
85
|
+
if (databaseType === 'mongodb') {
|
|
86
|
+
const mongoCollection = db.collection(collName);
|
|
87
|
+
documents = await mongoCollection.find({}).toArray();
|
|
88
|
+
} else if (databaseType === 'postgresql' || databaseType === 'postgres') {
|
|
89
|
+
const query = `SELECT ${columns.join(', ')} FROM ${collName}`;
|
|
90
|
+
const result = await client.query(query);
|
|
91
|
+
documents = result.rows;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const doc of documents) {
|
|
95
|
+
const text = columns
|
|
96
|
+
.map((col: string) => {
|
|
97
|
+
const value = doc[col];
|
|
98
|
+
return value !== null && value !== undefined ? `${col}: ${value}` : '';
|
|
99
|
+
})
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.join(' ');
|
|
102
|
+
|
|
103
|
+
if (!text.trim()) continue;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Generate embedding
|
|
107
|
+
const embeddingResponse = await openai.embeddings.create({
|
|
108
|
+
model: 'text-embedding-3-small',
|
|
109
|
+
input: text,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const embedding = embeddingResponse.data[0].embedding;
|
|
113
|
+
const docId = `${collName}_${doc._id || doc.id || Date.now()}_${totalProcessed}`;
|
|
114
|
+
|
|
115
|
+
// Store in ChromaDB
|
|
116
|
+
await chromaCollection.add({
|
|
117
|
+
ids: [docId],
|
|
118
|
+
embeddings: [embedding],
|
|
119
|
+
documents: [text],
|
|
120
|
+
metadatas: [{
|
|
121
|
+
collection: collName,
|
|
122
|
+
documentId: (doc._id || doc.id || totalProcessed).toString(),
|
|
123
|
+
columns: columns.join(','),
|
|
124
|
+
}],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
totalProcessed++;
|
|
128
|
+
} catch (error: any) {
|
|
129
|
+
console.error(`Error processing document in ${collName}:`, error);
|
|
130
|
+
// Continue with next document
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Close database connection
|
|
136
|
+
if (databaseType === 'mongodb') {
|
|
137
|
+
await client.close();
|
|
138
|
+
} else if (databaseType === 'postgresql' || databaseType === 'postgres') {
|
|
139
|
+
await client.end();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return NextResponse.json({
|
|
143
|
+
success: true,
|
|
144
|
+
message: `Successfully processed ${totalProcessed} documents`,
|
|
145
|
+
totalProcessed,
|
|
146
|
+
});
|
|
147
|
+
} catch (error: any) {
|
|
148
|
+
console.error('Process embeddings error:', error);
|
|
149
|
+
return NextResponse.json(
|
|
150
|
+
{ success: false, message: error.message || 'Failed to process embeddings' },
|
|
151
|
+
{ status: 500 }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { MongoClient } from 'mongodb';
|
|
3
|
+
import { Client } from 'pg';
|
|
4
|
+
|
|
5
|
+
export async function POST(request: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
const { databaseType, connection } = await request.json();
|
|
8
|
+
|
|
9
|
+
if (!databaseType || !connection) {
|
|
10
|
+
return NextResponse.json(
|
|
11
|
+
{ success: false, message: 'Database type and connection details are required' },
|
|
12
|
+
{ status: 400 }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (databaseType === 'mongodb') {
|
|
17
|
+
try {
|
|
18
|
+
const uri = connection.connectionString ||
|
|
19
|
+
`mongodb://${connection.username ? `${connection.username}:${connection.password}@` : ''}${connection.host}:${connection.port || 27017}/${connection.database}`;
|
|
20
|
+
|
|
21
|
+
const client = new MongoClient(uri);
|
|
22
|
+
await client.connect();
|
|
23
|
+
await client.db().admin().ping();
|
|
24
|
+
await client.close();
|
|
25
|
+
|
|
26
|
+
return NextResponse.json({ success: true, message: 'Connection successful' });
|
|
27
|
+
} catch (error: any) {
|
|
28
|
+
return NextResponse.json(
|
|
29
|
+
{ success: false, message: error.message || 'MongoDB connection failed' },
|
|
30
|
+
{ status: 500 }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
} else if (databaseType === 'postgresql') {
|
|
34
|
+
try {
|
|
35
|
+
const client = new Client({
|
|
36
|
+
host: connection.host,
|
|
37
|
+
port: connection.port || 5432,
|
|
38
|
+
database: connection.database,
|
|
39
|
+
user: connection.username,
|
|
40
|
+
password: connection.password,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await client.connect();
|
|
44
|
+
await client.query('SELECT 1');
|
|
45
|
+
await client.end();
|
|
46
|
+
|
|
47
|
+
return NextResponse.json({ success: true, message: 'Connection successful' });
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ success: false, message: error.message || 'PostgreSQL connection failed' },
|
|
51
|
+
{ status: 500 }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
return NextResponse.json(
|
|
56
|
+
{ success: false, message: 'Unsupported database type' },
|
|
57
|
+
{ status: 400 }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
} catch (error: any) {
|
|
61
|
+
console.error('Test connection error:', error);
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ success: false, message: error.message || 'Connection test failed' },
|
|
64
|
+
{ status: 500 }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|