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 CHANGED
@@ -1,6 +1,27 @@
1
- # Chatbot UI Component
1
+ # nextjs-chatbot-ui
2
2
 
3
- A beautiful, configurable chatbot UI component for Next.js applications with Tailwind CSS. Inspired by Sendbird's modern chat interface.
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
- Your backend should accept POST requests with the following format:
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:** The `suggestedReplies` or `suggestions` field is optional and will display as clickable buttons below the bot's message, similar to Sendbird's interface.
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
+ }