nextjs-chatbot-ui 1.4.1 → 1.6.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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # nextjs-chatbot-ui
2
2
 
3
- A beautiful, configurable chatbot UI component for Next.js with Tailwind CSS. Perfect for customer support, AI assistants, and chat applications.
3
+ A beautiful, **self-contained** chatbot UI component for Next.js with Tailwind CSS. Perfect for customer support, AI assistants, and chat applications.
4
+
5
+ **✨ All dependencies included!** When you install this package, all required dependencies (`mongodb`, `pg`, `chromadb`, `openai`) are automatically installed - no need to install anything else!
4
6
 
5
7
  ## ⚡ Super Simple Usage (Just 2 Files!)
6
8
 
@@ -54,6 +56,49 @@ This package requires:
54
56
  npm install nextjs-chatbot-ui
55
57
  ```
56
58
 
59
+ ## 🚀 Super Simple Setup (Complete RAG Chatbot in 3 Steps!)
60
+
61
+ **Everything works automatically!** All dependencies are included - just run the setup script and add your `.env` file.
62
+
63
+ ### Step 1: Run Setup Script
64
+
65
+ ```bash
66
+ npx nextjs-chatbot-ui-setup
67
+ ```
68
+
69
+ This automatically creates all API routes needed for the chatbot and admin setup.
70
+
71
+ ### Step 2: Add Environment Variables
72
+
73
+ Create `.env.local` file:
74
+ ```env
75
+ OPENAI_API_KEY=your_openai_api_key_here
76
+ CHROMADB_URL=http://localhost:8000
77
+ ```
78
+
79
+ ### Step 3: Use Components
80
+
81
+ ```javascript
82
+ // app/page.tsx
83
+ 'use client';
84
+ import { Chatbot, AdminSetup } from 'nextjs-chatbot-ui';
85
+
86
+ export default function Home() {
87
+ return (
88
+ <div>
89
+ <AdminSetup />
90
+ <Chatbot config={{ backendUrl: '/api/chat' }} />
91
+ </div>
92
+ );
93
+ }
94
+ ```
95
+
96
+ **That's it!** 🎉
97
+
98
+ - Use `<AdminSetup />` to connect to your database and process embeddings
99
+ - Use `<Chatbot />` to ask questions about your data
100
+ - Everything else is handled automatically!
101
+
57
102
  ## Quick Start (Minimal Code)
58
103
 
59
104
  ### Simplest Usage (Just 2 files!)
@@ -569,9 +614,10 @@ React UI → Express API → MongoDB + ChromaDB → OpenAI
569
614
 
570
615
  ### Quick Start with RAG
571
616
 
572
- 1. **Set up backend** (see `server.js` or `example-nextjs-api-routes.md`):
573
- - Express server with MongoDB, ChromaDB, and OpenAI integration
574
- - Endpoints: `/api/search`, `/api/database/process-embeddings`
617
+ 1. **Set up backend** (run the setup script):
618
+ - Run `npx nextjs-chatbot-ui-setup` to automatically create all API routes
619
+ - This creates Next.js API routes with MongoDB, ChromaDB, and OpenAI integration
620
+ - Endpoints: `/api/chat`, `/api/database/test`, `/api/database/collections`, `/api/database/columns`, `/api/database/process-embeddings`
575
621
 
576
622
  2. **Configure chatbot**:
577
623
  ```javascript
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
+ }
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "nextjs-chatbot-ui",
3
- "version": "1.4.1",
4
- "description": "A configurable chatbot UI component for Next.js with Tailwind CSS",
3
+ "version": "1.6.0",
4
+ "description": "A self-contained, configurable chatbot UI component for Next.js with Tailwind CSS - Complete RAG chatbot with automatic setup. All dependencies included!",
5
5
  "main": "./index.tsx",
6
6
  "module": "./index.tsx",
7
7
  "types": "./types/index.ts",
8
+ "bin": {
9
+ "nextjs-chatbot-ui-setup": "./scripts/setup.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./types/index.ts",
@@ -40,13 +43,16 @@
40
43
  "types",
41
44
  "utils",
42
45
  "hooks",
46
+ "api",
47
+ "scripts",
43
48
  "index.tsx",
44
49
  "README.md"
45
50
  ],
46
51
  "scripts": {
47
52
  "build": "echo 'Package is ready to use. Source files are included.'",
48
53
  "prepare": "npm run build",
49
- "type-check": "tsc --noEmit"
54
+ "type-check": "tsc --noEmit",
55
+ "setup": "node scripts/setup.js"
50
56
  },
51
57
  "keywords": [
52
58
  "chatbot",
@@ -64,10 +70,15 @@
64
70
  "next": "^13.0.0 || ^14.0.0 || ^15.0.0"
65
71
  },
66
72
  "dependencies": {
67
- "clsx": "^2.1.0"
73
+ "clsx": "^2.1.0",
74
+ "mongodb": "^6.0.0",
75
+ "pg": "^8.11.0",
76
+ "chromadb": "^1.8.0",
77
+ "openai": "^4.0.0"
68
78
  },
69
79
  "devDependencies": {
70
80
  "@types/node": "^20.0.0",
81
+ "@types/pg": "^8.10.0",
71
82
  "@types/react": "^18.0.0",
72
83
  "@types/react-dom": "^18.0.0",
73
84
  "autoprefixer": "^10.4.16",
@@ -0,0 +1,550 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ console.log('🚀 Setting up nextjs-chatbot-ui API routes...\n');
7
+
8
+ const projectRoot = process.cwd();
9
+ const isAppRouter = fs.existsSync(path.join(projectRoot, 'app'));
10
+
11
+ if (!isAppRouter && !fs.existsSync(path.join(projectRoot, 'pages'))) {
12
+ console.error('❌ Error: Next.js project not found. Please run this in a Next.js project directory.');
13
+ process.exit(1);
14
+ }
15
+
16
+ const apiDir = isAppRouter
17
+ ? path.join(projectRoot, 'app', 'api')
18
+ : path.join(projectRoot, 'pages', 'api');
19
+
20
+ // Create API directory if it doesn't exist
21
+ if (!fs.existsSync(apiDir)) {
22
+ fs.mkdirSync(apiDir, { recursive: true });
23
+ }
24
+
25
+ // API route templates
26
+ const routes = {
27
+ 'chat': `import { NextRequest, NextResponse } from 'next/server';
28
+ import { ChromaClient } from 'chromadb';
29
+ import OpenAI from 'openai';
30
+
31
+ const openai = new OpenAI({
32
+ apiKey: process.env.OPENAI_API_KEY,
33
+ });
34
+
35
+ const chromaClient = new ChromaClient({
36
+ path: process.env.CHROMADB_URL || 'http://localhost:8000',
37
+ });
38
+
39
+ export async function POST(request: NextRequest) {
40
+ try {
41
+ const { message, userInfo } = await request.json();
42
+
43
+ if (!message) {
44
+ return NextResponse.json(
45
+ { error: 'Message is required' },
46
+ { status: 400 }
47
+ );
48
+ }
49
+
50
+ if (!process.env.OPENAI_API_KEY) {
51
+ return NextResponse.json(
52
+ { error: 'OPENAI_API_KEY is not set in environment variables' },
53
+ { status: 500 }
54
+ );
55
+ }
56
+
57
+ // Get query embedding
58
+ const queryEmbedding = await openai.embeddings.create({
59
+ model: 'text-embedding-3-small',
60
+ input: message,
61
+ });
62
+
63
+ // Search in ChromaDB
64
+ const collectionName = process.env.CHROMA_COLLECTION_NAME || 'db_default';
65
+ let chromaCollection;
66
+
67
+ try {
68
+ chromaCollection = await chromaClient.getCollection({
69
+ name: collectionName,
70
+ });
71
+ } catch (error) {
72
+ return NextResponse.json({
73
+ message: 'Vector database not initialized. Please use AdminSetup to process embeddings first.',
74
+ response: 'Vector database not initialized. Please use AdminSetup to process embeddings first.',
75
+ });
76
+ }
77
+
78
+ const results = await chromaCollection.query({
79
+ queryEmbeddings: [queryEmbedding.data[0].embedding],
80
+ nResults: 5,
81
+ });
82
+
83
+ const contexts = results.documents[0] || [];
84
+ const contextText = contexts.length > 0
85
+ ? contexts.join('\\n\\n')
86
+ : 'No relevant context found.';
87
+
88
+ const completion = await openai.chat.completions.create({
89
+ model: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
90
+ messages: [
91
+ {
92
+ role: 'system',
93
+ 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.',
94
+ },
95
+ {
96
+ role: 'user',
97
+ content: \`Context:\\n\${contextText}\\n\\nQuestion: \${message}\\n\\nAnswer:\`,
98
+ },
99
+ ],
100
+ temperature: 0.7,
101
+ max_tokens: 500,
102
+ });
103
+
104
+ const answer = completion.choices[0].message.content;
105
+
106
+ return NextResponse.json({
107
+ message: answer,
108
+ response: answer,
109
+ });
110
+ } catch (error: any) {
111
+ console.error('Chat error:', error);
112
+ return NextResponse.json(
113
+ {
114
+ message: error.message || 'Failed to process message',
115
+ response: error.message || 'Failed to process message',
116
+ },
117
+ { status: 500 }
118
+ );
119
+ }
120
+ }
121
+ `,
122
+ 'database/test': `import { NextRequest, NextResponse } from 'next/server';
123
+ import { MongoClient } from 'mongodb';
124
+ import { Client } from 'pg';
125
+
126
+ export async function POST(request: NextRequest) {
127
+ try {
128
+ const { databaseType, connection } = await request.json();
129
+
130
+ if (!databaseType || !connection) {
131
+ return NextResponse.json(
132
+ { success: false, message: 'Database type and connection details are required' },
133
+ { status: 400 }
134
+ );
135
+ }
136
+
137
+ if (databaseType === 'mongodb') {
138
+ try {
139
+ const uri = connection.connectionString ||
140
+ \`mongodb://\${connection.username ? \`\${connection.username}:\${connection.password}@\` : ''}\${connection.host}:\${connection.port || 27017}/\${connection.database}\`;
141
+
142
+ const client = new MongoClient(uri);
143
+ await client.connect();
144
+ await client.db().admin().ping();
145
+ await client.close();
146
+
147
+ return NextResponse.json({ success: true, message: 'Connection successful' });
148
+ } catch (error: any) {
149
+ return NextResponse.json(
150
+ { success: false, message: error.message || 'MongoDB connection failed' },
151
+ { status: 500 }
152
+ );
153
+ }
154
+ } else if (databaseType === 'postgresql') {
155
+ try {
156
+ const client = new Client({
157
+ host: connection.host,
158
+ port: connection.port || 5432,
159
+ database: connection.database,
160
+ user: connection.username,
161
+ password: connection.password,
162
+ });
163
+
164
+ await client.connect();
165
+ await client.query('SELECT 1');
166
+ await client.end();
167
+
168
+ return NextResponse.json({ success: true, message: 'Connection successful' });
169
+ } catch (error: any) {
170
+ return NextResponse.json(
171
+ { success: false, message: error.message || 'PostgreSQL connection failed' },
172
+ { status: 500 }
173
+ );
174
+ }
175
+ } else {
176
+ return NextResponse.json(
177
+ { success: false, message: 'Unsupported database type' },
178
+ { status: 400 }
179
+ );
180
+ }
181
+ } catch (error: any) {
182
+ console.error('Test connection error:', error);
183
+ return NextResponse.json(
184
+ { success: false, message: error.message || 'Connection test failed' },
185
+ { status: 500 }
186
+ );
187
+ }
188
+ }
189
+ `,
190
+ 'database/collections': `import { NextRequest, NextResponse } from 'next/server';
191
+ import { MongoClient } from 'mongodb';
192
+ import { Client } from 'pg';
193
+
194
+ export async function POST(request: NextRequest) {
195
+ try {
196
+ const { databaseType, connection } = await request.json();
197
+
198
+ if (!databaseType || !connection) {
199
+ return NextResponse.json(
200
+ { collections: [], tables: [] },
201
+ { status: 400 }
202
+ );
203
+ }
204
+
205
+ if (databaseType === 'mongodb') {
206
+ try {
207
+ const uri = connection.connectionString ||
208
+ \`mongodb://\${connection.username ? \`\${connection.username}:\${connection.password}@\` : ''}\${connection.host}:\${connection.port || 27017}/\${connection.database}\`;
209
+
210
+ const client = new MongoClient(uri);
211
+ await client.connect();
212
+ const db = client.db(connection.database);
213
+ const collections = await db.listCollections().toArray();
214
+ await client.close();
215
+
216
+ return NextResponse.json({
217
+ collections: collections.map(c => c.name),
218
+ tables: collections.map(c => c.name),
219
+ });
220
+ } catch (error: any) {
221
+ return NextResponse.json(
222
+ { collections: [], tables: [], error: error.message },
223
+ { status: 500 }
224
+ );
225
+ }
226
+ } else if (databaseType === 'postgresql') {
227
+ try {
228
+ const client = new Client({
229
+ host: connection.host,
230
+ port: connection.port || 5432,
231
+ database: connection.database,
232
+ user: connection.username,
233
+ password: connection.password,
234
+ });
235
+
236
+ await client.connect();
237
+ const result = await client.query(\`
238
+ SELECT table_name
239
+ FROM information_schema.tables
240
+ WHERE table_schema = 'public'
241
+ \`);
242
+ await client.end();
243
+
244
+ const tables = result.rows.map(row => row.table_name);
245
+
246
+ return NextResponse.json({
247
+ collections: tables,
248
+ tables: tables,
249
+ });
250
+ } catch (error: any) {
251
+ return NextResponse.json(
252
+ { collections: [], tables: [], error: error.message },
253
+ { status: 500 }
254
+ );
255
+ }
256
+ } else {
257
+ return NextResponse.json(
258
+ { collections: [], tables: [] },
259
+ { status: 400 }
260
+ );
261
+ }
262
+ } catch (error: any) {
263
+ console.error('Fetch collections error:', error);
264
+ return NextResponse.json(
265
+ { collections: [], tables: [], error: error.message },
266
+ { status: 500 }
267
+ );
268
+ }
269
+ }
270
+ `,
271
+ 'database/columns': `import { NextRequest, NextResponse } from 'next/server';
272
+ import { MongoClient } from 'mongodb';
273
+ import { Client } from 'pg';
274
+
275
+ export async function POST(request: NextRequest) {
276
+ try {
277
+ const { databaseType, connection, collection } = await request.json();
278
+
279
+ if (!databaseType || !connection || !collection) {
280
+ return NextResponse.json(
281
+ { columns: [] },
282
+ { status: 400 }
283
+ );
284
+ }
285
+
286
+ if (databaseType === 'mongodb') {
287
+ try {
288
+ const uri = connection.connectionString ||
289
+ \`mongodb://\${connection.username ? \`\${connection.username}:\${connection.password}@\` : ''}\${connection.host}:\${connection.port || 27017}/\${connection.database}\`;
290
+
291
+ const client = new MongoClient(uri);
292
+ await client.connect();
293
+ const db = client.db(connection.database);
294
+ const mongoCollection = db.collection(collection);
295
+
296
+ const sampleDoc = await mongoCollection.findOne({});
297
+ await client.close();
298
+
299
+ const columns = sampleDoc ? Object.keys(sampleDoc).filter(key => key !== '_id') : [];
300
+
301
+ return NextResponse.json({ columns });
302
+ } catch (error: any) {
303
+ return NextResponse.json(
304
+ { columns: [], error: error.message },
305
+ { status: 500 }
306
+ );
307
+ }
308
+ } else if (databaseType === 'postgresql') {
309
+ try {
310
+ const client = new Client({
311
+ host: connection.host,
312
+ port: connection.port || 5432,
313
+ database: connection.database,
314
+ user: connection.username,
315
+ password: connection.password,
316
+ });
317
+
318
+ await client.connect();
319
+ const result = await client.query(\`
320
+ SELECT column_name
321
+ FROM information_schema.columns
322
+ WHERE table_schema = 'public'
323
+ AND table_name = $1
324
+ \`, [collection]);
325
+ await client.end();
326
+
327
+ const columns = result.rows.map(row => row.column_name);
328
+
329
+ return NextResponse.json({ columns });
330
+ } catch (error: any) {
331
+ return NextResponse.json(
332
+ { columns: [], error: error.message },
333
+ { status: 500 }
334
+ );
335
+ }
336
+ } else {
337
+ return NextResponse.json(
338
+ { columns: [] },
339
+ { status: 400 }
340
+ );
341
+ }
342
+ } catch (error: any) {
343
+ console.error('Fetch columns error:', error);
344
+ return NextResponse.json(
345
+ { columns: [], error: error.message },
346
+ { status: 500 }
347
+ );
348
+ }
349
+ }
350
+ `,
351
+ 'database/process-embeddings': `import { NextRequest, NextResponse } from 'next/server';
352
+ import { MongoClient } from 'mongodb';
353
+ import { Client } from 'pg';
354
+ import { ChromaClient } from 'chromadb';
355
+ import OpenAI from 'openai';
356
+
357
+ const openai = new OpenAI({
358
+ apiKey: process.env.OPENAI_API_KEY,
359
+ });
360
+
361
+ const chromaClient = new ChromaClient({
362
+ path: process.env.CHROMADB_URL || 'http://localhost:8000',
363
+ });
364
+
365
+ export async function POST(request: NextRequest) {
366
+ try {
367
+ const { connection, collections, columnSelection } = await request.json();
368
+
369
+ if (!process.env.OPENAI_API_KEY) {
370
+ return NextResponse.json(
371
+ { success: false, message: 'OPENAI_API_KEY is not set in environment variables' },
372
+ { status: 500 }
373
+ );
374
+ }
375
+
376
+ if (!connection || !collections || !Array.isArray(collections) || collections.length === 0) {
377
+ return NextResponse.json(
378
+ { success: false, message: 'Connection and collections are required' },
379
+ { status: 400 }
380
+ );
381
+ }
382
+
383
+ const databaseType = connection.type || 'mongodb';
384
+ const dbName = connection.database;
385
+
386
+ let db: any;
387
+ let client: any;
388
+
389
+ if (databaseType === 'mongodb') {
390
+ const uri = connection.connectionString ||
391
+ \`mongodb://\${connection.username ? \`\${connection.username}:\${connection.password}@\` : ''}\${connection.host}:\${connection.port || 27017}/\${dbName}\`;
392
+ client = new MongoClient(uri);
393
+ await client.connect();
394
+ db = client.db(dbName);
395
+ } else if (databaseType === 'postgresql' || databaseType === 'postgres') {
396
+ client = new Client({
397
+ host: connection.host,
398
+ port: connection.port || 5432,
399
+ database: dbName,
400
+ user: connection.username,
401
+ password: connection.password,
402
+ });
403
+ await client.connect();
404
+ } else {
405
+ return NextResponse.json(
406
+ { success: false, message: 'Unsupported database type' },
407
+ { status: 400 }
408
+ );
409
+ }
410
+
411
+ const collectionName = process.env.CHROMA_COLLECTION_NAME || \`db_\${dbName}\`;
412
+ let chromaCollection;
413
+ try {
414
+ chromaCollection = await chromaClient.getOrCreateCollection({
415
+ name: collectionName,
416
+ });
417
+ } catch (error) {
418
+ chromaCollection = await chromaClient.createCollection({
419
+ name: collectionName,
420
+ });
421
+ }
422
+
423
+ let totalProcessed = 0;
424
+
425
+ for (const collConfig of collections) {
426
+ const { collection: collName, columns } = collConfig;
427
+
428
+ if (!columns || columns.length === 0) continue;
429
+
430
+ let documents: any[] = [];
431
+
432
+ if (databaseType === 'mongodb') {
433
+ const mongoCollection = db.collection(collName);
434
+ documents = await mongoCollection.find({}).toArray();
435
+ } else if (databaseType === 'postgresql' || databaseType === 'postgres') {
436
+ const query = \`SELECT \${columns.join(', ')} FROM \${collName}\`;
437
+ const result = await client.query(query);
438
+ documents = result.rows;
439
+ }
440
+
441
+ for (const doc of documents) {
442
+ const text = columns
443
+ .map((col: string) => {
444
+ const value = doc[col];
445
+ return value !== null && value !== undefined ? \`\${col}: \${value}\` : '';
446
+ })
447
+ .filter(Boolean)
448
+ .join(' ');
449
+
450
+ if (!text.trim()) continue;
451
+
452
+ try {
453
+ const embeddingResponse = await openai.embeddings.create({
454
+ model: 'text-embedding-3-small',
455
+ input: text,
456
+ });
457
+
458
+ const embedding = embeddingResponse.data[0].embedding;
459
+ const docId = \`\${collName}_\${doc._id || doc.id || Date.now()}_\${totalProcessed}\`;
460
+
461
+ await chromaCollection.add({
462
+ ids: [docId],
463
+ embeddings: [embedding],
464
+ documents: [text],
465
+ metadatas: [{
466
+ collection: collName,
467
+ documentId: (doc._id || doc.id || totalProcessed).toString(),
468
+ columns: columns.join(','),
469
+ }],
470
+ });
471
+
472
+ totalProcessed++;
473
+ } catch (error: any) {
474
+ console.error(\`Error processing document in \${collName}:\`, error);
475
+ }
476
+ }
477
+ }
478
+
479
+ if (databaseType === 'mongodb') {
480
+ await client.close();
481
+ } else if (databaseType === 'postgresql' || databaseType === 'postgres') {
482
+ await client.end();
483
+ }
484
+
485
+ return NextResponse.json({
486
+ success: true,
487
+ message: \`Successfully processed \${totalProcessed} documents\`,
488
+ totalProcessed,
489
+ });
490
+ } catch (error: any) {
491
+ console.error('Process embeddings error:', error);
492
+ return NextResponse.json(
493
+ { success: false, message: error.message || 'Failed to process embeddings' },
494
+ { status: 500 }
495
+ );
496
+ }
497
+ }
498
+ `,
499
+ };
500
+
501
+ // Create routes
502
+ let createdCount = 0;
503
+ for (const [routePath, content] of Object.entries(routes)) {
504
+ const routeDir = path.join(apiDir, routePath);
505
+ const routeFile = isAppRouter
506
+ ? path.join(routeDir, 'route.ts')
507
+ : path.join(routeDir, 'index.ts');
508
+
509
+ // Create directory if it doesn't exist
510
+ if (!fs.existsSync(routeDir)) {
511
+ fs.mkdirSync(routeDir, { recursive: true });
512
+ }
513
+
514
+ // Check if file already exists
515
+ if (fs.existsSync(routeFile)) {
516
+ console.log(\`⏭️ Skipping \${routePath} (already exists)\`);
517
+ continue;
518
+ }
519
+
520
+ // Write route file
521
+ fs.writeFileSync(routeFile, content, 'utf8');
522
+ console.log(\`✅ Created \${routePath}\`);
523
+ createdCount++;
524
+ }
525
+
526
+ // Create .env.example if it doesn't exist
527
+ const envExamplePath = path.join(projectRoot, '.env.example');
528
+ if (!fs.existsSync(envExamplePath)) {
529
+ const envExample = \`# OpenAI API Key
530
+ OPENAI_API_KEY=your_openai_api_key_here
531
+
532
+ # ChromaDB URL (default: http://localhost:8000)
533
+ CHROMADB_URL=http://localhost:8000
534
+
535
+ # Optional: ChromaDB Collection Name (default: db_<database_name>)
536
+ # CHROMA_COLLECTION_NAME=db_default
537
+
538
+ # Optional: OpenAI Model (default: gpt-3.5-turbo)
539
+ # OPENAI_MODEL=gpt-3.5-turbo
540
+ \`;
541
+ fs.writeFileSync(envExamplePath, envExample, 'utf8');
542
+ console.log('✅ Created .env.example');
543
+ }
544
+
545
+ console.log(\`\\n✨ Setup complete! \${createdCount} API route(s) created.\`);
546
+ console.log('\\n📝 Next steps:');
547
+ console.log('1. Copy .env.example to .env.local and add your API keys');
548
+ console.log('2. Install dependencies: npm install mongodb pg chromadb openai');
549
+ console.log('3. Use <Chatbot /> and <AdminSetup /> components in your app');
550
+ console.log('\\n🎉 You\\'re all set!');