mirror of
https://github.com/coleam00/ai-agents-masterclass.git
synced 2026-01-19 21:40:32 +00:00
V0 + Claude - RAG AI Agent Frontend
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,10 +1,12 @@
|
||||
__pycache__
|
||||
prep
|
||||
.env
|
||||
.env.local
|
||||
chroma_db
|
||||
test.py
|
||||
data
|
||||
creds
|
||||
credentials
|
||||
credentials.json
|
||||
token.json
|
||||
token.json
|
||||
node_modules
|
||||
21
v0-agent-frontend/Prompts.txt
Normal file
21
v0-agent-frontend/Prompts.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Prompt 1:
|
||||
|
||||
Create a full chatbot page where the chat will automatically scroll up as the conversation continues like a chat you'd have in FB messenger, Discord, Slack, etc. Every time the user enters a message, just respond with "placeholder message" after thinking for a couple of seconds.
|
||||
|
||||
Prompt 2:
|
||||
|
||||
Now change the chatbot so I can talk to my AI Agent through a webhook and still have the chat automatically scroll up as the conversation continues like a chat you'd have in FB messenger, Discord, Slack, etc.
|
||||
|
||||
The webhook to invoke for the AI Agent is:
|
||||
|
||||
[your webhook endpoint]
|
||||
|
||||
Include an authorization header with the authorization [your bearer token, this is optional if you turn it off in n8n]. The payload needs to include "sessionId" and "chatInput". Randomly generate a string for the sessionId and the chatInput is the user input in the frontend. The webhook will respond with a single object "output" in the JSON to display back to the user as the agent's response.
|
||||
|
||||
Prompt 3:
|
||||
|
||||
Parse the AI responses as Markdown so they look nice. Also make the UI a dark theme and have a navigation header with a bottom shadow. Have a couple of icons for navigation on the left and the title on the right. Also include a side bar on the left with some example conversations like you would see on chatgpt.com.
|
||||
|
||||
Prompt 4:
|
||||
|
||||
Add Supabase authentication into this component so someone has to sign in to get access to the chatbot and then the session ID can be the Supabase user ID.
|
||||
740
v0-agent-frontend/Supabase_RAG_AI_Agent_n8n_Workflow.json
Normal file
740
v0-agent-frontend/Supabase_RAG_AI_Agent_n8n_Workflow.json
Normal file
@@ -0,0 +1,740 @@
|
||||
{
|
||||
"name": "Supabase RAG AI Agent",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"model": "gpt-4o-mini",
|
||||
"options": {}
|
||||
},
|
||||
"id": "b98c17c5-3bef-4378-8d81-61b127711661",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
860,
|
||||
520
|
||||
],
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "JJjD91oisPv9cs01",
|
||||
"name": "OpenAi account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"model": "gpt-4o-mini",
|
||||
"options": {}
|
||||
},
|
||||
"id": "e850aa69-1910-4986-99f2-214a8759b777",
|
||||
"name": "OpenAI Chat Model1",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1880,
|
||||
460
|
||||
],
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "JJjD91oisPv9cs01",
|
||||
"name": "OpenAi account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsonMode": "expressionData",
|
||||
"jsonData": "={{ $json.data }}",
|
||||
"options": {
|
||||
"metadata": {
|
||||
"metadataValues": [
|
||||
{
|
||||
"name": "=file_id",
|
||||
"value": "={{ $('Set File ID').item.json.file_id }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "9f5b6c56-58f6-4ae5-96fd-f3efd9b35fd6",
|
||||
"name": "Default Data Loader",
|
||||
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
2020,
|
||||
1000
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"model": "text-embedding-3-small",
|
||||
"options": {}
|
||||
},
|
||||
"id": "5eaf6315-fcd0-4a4d-a1b3-37f5793f6e5c",
|
||||
"name": "Embeddings OpenAI1",
|
||||
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1860,
|
||||
1000
|
||||
],
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "JJjD91oisPv9cs01",
|
||||
"name": "OpenAi account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"name": "user_documents",
|
||||
"description": "Contains all the user's documents that you can check for context to answer user questions."
|
||||
},
|
||||
"id": "b3868e8d-fbce-4c16-8174-0e5475cfffba",
|
||||
"name": "Retrieve Documents",
|
||||
"type": "@n8n/n8n-nodes-langchain.toolVectorStore",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1660,
|
||||
280
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Agent Tools for RAG",
|
||||
"height": 528.85546469693,
|
||||
"width": 583.4552380860637,
|
||||
"color": 4
|
||||
},
|
||||
"id": "d4a657c6-2f17-4071-8cee-0b1b12f74b1c",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1480,
|
||||
160
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Tool to Add a Google Drive File to Vector DB",
|
||||
"height": 671.8877842322804,
|
||||
"width": 2070.8894079025763,
|
||||
"color": 5
|
||||
},
|
||||
"id": "38517d9e-61cf-4b36-bbb2-607a04e870c8",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
260,
|
||||
700
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "download",
|
||||
"fileId": {
|
||||
"__rl": true,
|
||||
"value": "={{ $('Set File ID').item.json.file_id }}",
|
||||
"mode": "id"
|
||||
},
|
||||
"options": {
|
||||
"googleFileConversion": {
|
||||
"conversion": {
|
||||
"docsToFormat": "text/plain"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "d9e3d812-9158-44e2-b9e9-8c28c5cd0921",
|
||||
"name": "Download File",
|
||||
"type": "n8n-nodes-base.googleDrive",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
1360,
|
||||
880
|
||||
],
|
||||
"executeOnce": true,
|
||||
"credentials": {
|
||||
"googleDriveOAuth2Api": {
|
||||
"id": "cfNochbuJikPwwl2",
|
||||
"name": "Google Drive account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
},
|
||||
"triggerOn": "specificFolder",
|
||||
"folderToWatch": {
|
||||
"__rl": true,
|
||||
"value": "1914m3M7kRzkd5RJqAfzRY9EBcJrKemZC",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Meeting Notes",
|
||||
"cachedResultUrl": "https://drive.google.com/drive/folders/1914m3M7kRzkd5RJqAfzRY9EBcJrKemZC"
|
||||
},
|
||||
"event": "fileCreated",
|
||||
"options": {}
|
||||
},
|
||||
"id": "6c24f48d-2a8e-471d-b477-dd9576e57eec",
|
||||
"name": "File Created",
|
||||
"type": "n8n-nodes-base.googleDriveTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
320,
|
||||
780
|
||||
],
|
||||
"credentials": {
|
||||
"googleDriveOAuth2Api": {
|
||||
"id": "cfNochbuJikPwwl2",
|
||||
"name": "Google Drive account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
},
|
||||
"triggerOn": "specificFolder",
|
||||
"folderToWatch": {
|
||||
"__rl": true,
|
||||
"value": "1914m3M7kRzkd5RJqAfzRY9EBcJrKemZC",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Meeting Notes",
|
||||
"cachedResultUrl": "https://drive.google.com/drive/folders/1914m3M7kRzkd5RJqAfzRY9EBcJrKemZC"
|
||||
},
|
||||
"event": "fileUpdated",
|
||||
"options": {}
|
||||
},
|
||||
"id": "7bf930ea-3810-40e3-a252-c13da33660db",
|
||||
"name": "File Updated",
|
||||
"type": "n8n-nodes-base.googleDriveTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
320,
|
||||
1000
|
||||
],
|
||||
"credentials": {
|
||||
"googleDriveOAuth2Api": {
|
||||
"id": "cfNochbuJikPwwl2",
|
||||
"name": "Google Drive account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "text",
|
||||
"options": {}
|
||||
},
|
||||
"id": "81e0f199-024b-489d-88dd-e7bf6c3a453b",
|
||||
"name": "Extract Document Text",
|
||||
"type": "n8n-nodes-base.extractFromFile",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1620,
|
||||
880
|
||||
],
|
||||
"alwaysOutputData": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"model": "text-embedding-3-small",
|
||||
"options": {}
|
||||
},
|
||||
"id": "c0f6a1f6-5732-48e3-8830-bb78263bd352",
|
||||
"name": "Embeddings OpenAI",
|
||||
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1700,
|
||||
560
|
||||
],
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "JJjD91oisPv9cs01",
|
||||
"name": "OpenAi account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "8920aea0-c877-47b3-9026-d8ab57e8579d",
|
||||
"name": "Postgres Chat Memory",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1000,
|
||||
520
|
||||
],
|
||||
"notesInFlow": false,
|
||||
"credentials": {
|
||||
"postgres": {
|
||||
"id": "tzFFADvpFiUtaZtb",
|
||||
"name": "Supabase Postgres"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "ed70601e-e7e9-4fc5-af9f-bdf4a931fc48",
|
||||
"name": "Recursive Character Text Splitter",
|
||||
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
2020,
|
||||
1200
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "delete",
|
||||
"tableId": "documents",
|
||||
"filterType": "string",
|
||||
"filterString": "=metadata->>file_id=like.*{{ $json.file_id }}*"
|
||||
},
|
||||
"id": "bd9eb3dc-75b9-4577-a210-1951988a389e",
|
||||
"name": "Delete Old Doc Rows",
|
||||
"type": "n8n-nodes-base.supabase",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1060,
|
||||
880
|
||||
],
|
||||
"alwaysOutputData": true,
|
||||
"credentials": {
|
||||
"supabaseApi": {
|
||||
"id": "azIAMHxdSaq5XhoW",
|
||||
"name": "Supabase account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "10646eae-ae46-4327-a4dc-9987c2d76173",
|
||||
"name": "file_id",
|
||||
"value": "={{ $json.id }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "0b439eba-c467-475b-a1a2-481f636f37fe",
|
||||
"name": "Set File ID",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
740,
|
||||
880
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## RAG AI Agent with Chat Interface",
|
||||
"height": 464.8027193303974,
|
||||
"width": 1035.6381264595484
|
||||
},
|
||||
"id": "7c003acd-f420-474f-9547-33e13c3c357f",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
432.49413148117685,
|
||||
220
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"tableName": {
|
||||
"__rl": true,
|
||||
"value": "documents",
|
||||
"mode": "list",
|
||||
"cachedResultName": "documents"
|
||||
},
|
||||
"options": {
|
||||
"queryName": "match_documents"
|
||||
}
|
||||
},
|
||||
"id": "1bda5922-bcfb-489c-a0fb-516d9496d54a",
|
||||
"name": "Supabase Vector Store",
|
||||
"type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1540,
|
||||
440
|
||||
],
|
||||
"credentials": {
|
||||
"supabaseApi": {
|
||||
"id": "azIAMHxdSaq5XhoW",
|
||||
"name": "Supabase account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "insert",
|
||||
"tableName": {
|
||||
"__rl": true,
|
||||
"value": "documents",
|
||||
"mode": "list",
|
||||
"cachedResultName": "documents"
|
||||
},
|
||||
"options": {
|
||||
"queryName": "match_documents"
|
||||
}
|
||||
},
|
||||
"id": "6c7181ba-9e33-4972-83b4-0efaac7e16a2",
|
||||
"name": "Insert into Supabase Vectorstore",
|
||||
"type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1900,
|
||||
780
|
||||
],
|
||||
"credentials": {
|
||||
"supabaseApi": {
|
||||
"id": "azIAMHxdSaq5XhoW",
|
||||
"name": "Supabase account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "dd1086c5-e813-49ea-971f-ba01ccc6f803",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
1300,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"public": true,
|
||||
"options": {}
|
||||
},
|
||||
"id": "e448d46a-7a01-436c-bebd-0cd21c2bfcb3",
|
||||
"name": "When chat message received",
|
||||
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
480,
|
||||
300
|
||||
],
|
||||
"webhookId": "e985d15f-b2f6-456d-be15-97e0b1544a40"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "9a9a245e-f1a1-4282-bb02-a81ffe629f0f",
|
||||
"name": "chatInput",
|
||||
"value": "={{ $json?.chatInput || $json.body.chatInput }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b80831d8-c653-4203-8706-adedfdb98f77",
|
||||
"name": "sessionId",
|
||||
"value": "={{ $json?.sessionId || $json.body.sessionId}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "4076ba7c-931d-40f5-872d-c9b2dc669ab1",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
740,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"promptType": "define",
|
||||
"text": "={{ $json.chatInput }}",
|
||||
"options": {
|
||||
"systemMessage": "You are a personal assistant who helps answer questions from a corpus of documents when you don't know the answer yourself."
|
||||
}
|
||||
},
|
||||
"id": "50003c9d-29bf-4d0e-9e04-a49804742dc8",
|
||||
"name": "RAG AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 1.6,
|
||||
"position": [
|
||||
960,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "invoke_agent",
|
||||
"authentication": "headerAuth",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"id": "b575cc9f-71c7-4406-936c-ca39e6eff09b",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
480,
|
||||
500
|
||||
],
|
||||
"webhookId": "67fc16e5-4a6d-4c00-b7a1-db48c4511e9f",
|
||||
"credentials": {
|
||||
"httpHeaderAuth": {
|
||||
"id": "FWbnHWdNKFyEHa1W",
|
||||
"name": "Header Auth account"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "RAG AI Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model1": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "Retrieve Documents",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Retrieve Documents": {
|
||||
"ai_tool": [
|
||||
[
|
||||
{
|
||||
"node": "RAG AI Agent",
|
||||
"type": "ai_tool",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Download File": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Document Text",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"File Created": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set File ID",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Embeddings OpenAI": {
|
||||
"ai_embedding": [
|
||||
[
|
||||
{
|
||||
"node": "Supabase Vector Store",
|
||||
"type": "ai_embedding",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Document Text": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Insert into Supabase Vectorstore",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Embeddings OpenAI1": {
|
||||
"ai_embedding": [
|
||||
[
|
||||
{
|
||||
"node": "Insert into Supabase Vectorstore",
|
||||
"type": "ai_embedding",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Default Data Loader": {
|
||||
"ai_document": [
|
||||
[
|
||||
{
|
||||
"node": "Insert into Supabase Vectorstore",
|
||||
"type": "ai_document",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Postgres Chat Memory": {
|
||||
"ai_memory": [
|
||||
[
|
||||
{
|
||||
"node": "RAG AI Agent",
|
||||
"type": "ai_memory",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Recursive Character Text Splitter": {
|
||||
"ai_textSplitter": [
|
||||
[
|
||||
{
|
||||
"node": "Default Data Loader",
|
||||
"type": "ai_textSplitter",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Delete Old Doc Rows": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Download File",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set File ID": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Delete Old Doc Rows",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"File Updated": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set File ID",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Supabase Vector Store": {
|
||||
"ai_vectorStore": [
|
||||
[
|
||||
{
|
||||
"node": "Retrieve Documents",
|
||||
"type": "ai_vectorStore",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When chat message received": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "RAG AI Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"RAG AI Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "1f7ec7d0-ff5e-4448-b0b3-76f4e782baa2",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "620f0d7e3114cb344761d7d45a21ef2a32096f91d8696e7057756042e1999e2c"
|
||||
},
|
||||
"id": "cri0nBz0U55sWr4I",
|
||||
"tags": []
|
||||
}
|
||||
3
v0-agent-frontend/v0-agent-frontend/.env.local.example
Normal file
3
v0-agent-frontend/v0-agent-frontend/.env.local.example
Normal file
@@ -0,0 +1,3 @@
|
||||
# Rename this to .env.local after adding the two values below!
|
||||
NEXT_PUBLIC_SUPABASE_URL=
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
||||
3
v0-agent-frontend/v0-agent-frontend/.eslintrc.json
Normal file
3
v0-agent-frontend/v0-agent-frontend/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||
}
|
||||
36
v0-agent-frontend/v0-agent-frontend/.gitignore
vendored
Normal file
36
v0-agent-frontend/v0-agent-frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
36
v0-agent-frontend/v0-agent-frontend/README.md
Normal file
36
v0-agent-frontend/v0-agent-frontend/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const code = requestUrl.searchParams.get('code')
|
||||
|
||||
if (code) {
|
||||
const supabase = createRouteHandlerClient({ cookies })
|
||||
await supabase.auth.exchangeCodeForSession(code)
|
||||
}
|
||||
|
||||
// URL to redirect to after sign in process completes
|
||||
return NextResponse.redirect(requestUrl.origin)
|
||||
}
|
||||
BIN
v0-agent-frontend/v0-agent-frontend/app/favicon.ico
Normal file
BIN
v0-agent-frontend/v0-agent-frontend/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
v0-agent-frontend/v0-agent-frontend/app/fonts/GeistMonoVF.woff
Normal file
BIN
v0-agent-frontend/v0-agent-frontend/app/fonts/GeistMonoVF.woff
Normal file
Binary file not shown.
BIN
v0-agent-frontend/v0-agent-frontend/app/fonts/GeistVF.woff
Normal file
BIN
v0-agent-frontend/v0-agent-frontend/app/fonts/GeistVF.woff
Normal file
Binary file not shown.
33
v0-agent-frontend/v0-agent-frontend/app/globals.css
Normal file
33
v0-agent-frontend/v0-agent-frontend/app/globals.css
Normal file
@@ -0,0 +1,33 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
35
v0-agent-frontend/v0-agent-frontend/app/layout.tsx
Normal file
35
v0-agent-frontend/v0-agent-frontend/app/layout.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = localFont({
|
||||
src: "./fonts/GeistVF.woff",
|
||||
variable: "--font-geist-sans",
|
||||
weight: "100 900",
|
||||
});
|
||||
const geistMono = localFont({
|
||||
src: "./fonts/GeistMonoVF.woff",
|
||||
variable: "--font-geist-mono",
|
||||
weight: "100 900",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
5
v0-agent-frontend/v0-agent-frontend/app/page.tsx
Normal file
5
v0-agent-frontend/v0-agent-frontend/app/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import AiAgentChatbot from "@/components/ai-agent-chatbot"
|
||||
|
||||
export default function Page() {
|
||||
return <AiAgentChatbot />
|
||||
}
|
||||
20
v0-agent-frontend/v0-agent-frontend/components.json
Normal file
20
v0-agent-frontend/v0-agent-frontend/components.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": false,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { SendIcon, MenuIcon, HomeIcon, MessageCircleIcon, SettingsIcon, LogOutIcon } from 'lucide-react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { Session, AuthChangeEvent } from '@supabase/supabase-js'
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
import { ThemeSupa } from '@supabase/auth-ui-shared'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
type Message = {
|
||||
text: string
|
||||
sender: 'user' | 'bot'
|
||||
}
|
||||
|
||||
const exampleConversations = [
|
||||
{ title: "Getting Started", id: "getting-started" },
|
||||
{ title: "Task Planning", id: "task-planning" },
|
||||
{ title: "Problem Solving", id: "problem-solving" },
|
||||
{ title: "Creative Writing", id: "creative-writing" },
|
||||
]
|
||||
|
||||
export default function AIAgentChatbot() {
|
||||
const [messages, setMessages] = useState<Message[]>([])
|
||||
const [inputMessage, setInputMessage] = useState('')
|
||||
const [isTyping, setIsTyping] = useState(false)
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
|
||||
const [user, setUser] = useState<any>(null)
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
const supabase = createClientComponentClient()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
const getUser = async () => {
|
||||
const { data: { user } } = await supabase.auth.getUser()
|
||||
setUser(user)
|
||||
}
|
||||
getUser()
|
||||
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event: AuthChangeEvent, session: Session | null) => {
|
||||
setUser(session?.user ?? null)
|
||||
})
|
||||
|
||||
return () => subscription.unsubscribe()
|
||||
}, [supabase.auth])
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom()
|
||||
}, [messages])
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
|
||||
}
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await supabase.auth.signOut()
|
||||
router.refresh()
|
||||
}
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (inputMessage.trim() === '') return
|
||||
|
||||
const newUserMessage: Message = { text: inputMessage, sender: 'user' }
|
||||
setMessages(prevMessages => [...prevMessages, newUserMessage])
|
||||
setInputMessage('')
|
||||
setIsTyping(true)
|
||||
|
||||
try {
|
||||
const response = await fetch('[YOUR n8n WORKFLOW WEBHOOK URL]', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer [YOUR AUTH BEARER TOKEN]'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sessionId: user?.id,
|
||||
chatInput: inputMessage
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const botResponse: Message = { text: data.output, sender: 'bot' }
|
||||
setMessages(prevMessages => [...prevMessages, botResponse])
|
||||
} catch (error) {
|
||||
console.error('Error:', error)
|
||||
const errorMessage: Message = { text: 'Sorry, I encountered an error. Please try again.', sender: 'bot' }
|
||||
setMessages(prevMessages => [...prevMessages, errorMessage])
|
||||
} finally {
|
||||
setIsTyping(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-gray-900 text-gray-100">
|
||||
<div className="w-full max-w-md p-8 space-y-8 bg-gray-800 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-bold text-center">Sign In to AI Agent Chatbot</h2>
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
theme="dark"
|
||||
providers={['google', 'github']}
|
||||
redirectTo={`${window.location.origin}/auth/callback`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-900 text-gray-100">
|
||||
{/* Sidebar */}
|
||||
<div className={`fixed inset-y-0 left-0 z-50 w-64 bg-gray-800 transform ${isSidebarOpen ? 'translate-x-0' : '-translate-x-full'} transition-transform duration-300 ease-in-out lg:relative lg:translate-x-0`}>
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex items-center justify-center h-16 bg-gray-900">
|
||||
<h2 className="text-xl font-bold">Conversations</h2>
|
||||
</div>
|
||||
<ScrollArea className="flex-1">
|
||||
{exampleConversations.map((conv) => (
|
||||
<button
|
||||
key={conv.id}
|
||||
className="w-full text-left px-4 py-2 hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
|
||||
>
|
||||
{conv.title}
|
||||
</button>
|
||||
))}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main content */}
|
||||
<div className="flex flex-col flex-1">
|
||||
{/* Navigation header */}
|
||||
<header className="flex items-center justify-between px-4 h-16 bg-gray-800 shadow-md">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
||||
className="lg:hidden"
|
||||
>
|
||||
<MenuIcon className="h-6 w-6" />
|
||||
<span className="sr-only">Toggle sidebar</span>
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon">
|
||||
<HomeIcon className="h-6 w-6" />
|
||||
<span className="sr-only">Home</span>
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon">
|
||||
<MessageCircleIcon className="h-6 w-6" />
|
||||
<span className="sr-only">Messages</span>
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon">
|
||||
<SettingsIcon className="h-6 w-6" />
|
||||
<span className="sr-only">Settings</span>
|
||||
</Button>
|
||||
</div>
|
||||
<h1 className="text-xl font-bold">AI Agent Chatbot</h1>
|
||||
<Button variant="ghost" size="icon" onClick={handleSignOut}>
|
||||
<LogOutIcon className="h-6 w-6" />
|
||||
<span className="sr-only">Sign out</span>
|
||||
</Button>
|
||||
</header>
|
||||
|
||||
{/* Chat area */}
|
||||
<ScrollArea className="flex-grow p-4">
|
||||
{messages.map((message, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`mb-4 ${
|
||||
message.sender === 'user' ? 'text-right' : 'text-left'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block p-2 rounded-lg ${
|
||||
message.sender === 'user'
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-700 text-gray-100'
|
||||
}`}
|
||||
>
|
||||
{message.sender === 'bot' ? (
|
||||
<ReactMarkdown>{message.text}</ReactMarkdown>
|
||||
) : (
|
||||
message.text
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
{isTyping && (
|
||||
<div className="text-left mb-4">
|
||||
<span className="inline-block p-2 rounded-lg bg-gray-700 text-gray-100">
|
||||
Typing...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</ScrollArea>
|
||||
|
||||
{/* Input area */}
|
||||
<div className="p-4 border-t border-gray-700">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleSendMessage()
|
||||
}}
|
||||
className="flex space-x-2"
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Type your message..."
|
||||
value={inputMessage}
|
||||
onChange={(e) => setInputMessage(e.target.value)}
|
||||
className="flex-1 bg-gray-700 text-gray-100 border-gray-600 focus:border-blue-500"
|
||||
/>
|
||||
<Button type="submit" className="bg-blue-600 hover:bg-blue-700">
|
||||
<SendIcon className="h-4 w-4" />
|
||||
<span className="sr-only">Send</span>
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
59
v0-agent-frontend/v0-agent-frontend/components/ui/button.tsx
Normal file
59
v0-agent-frontend/v0-agent-frontend/components/ui/button.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-neutral-900 text-neutral-50 hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90",
|
||||
destructive:
|
||||
"bg-red-500 text-neutral-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/90",
|
||||
outline:
|
||||
"border border-neutral-200 bg-white hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
|
||||
secondary:
|
||||
"bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80",
|
||||
ghost: "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
|
||||
link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ButtonVariantProps = VariantProps<typeof buttonVariants>
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: ButtonVariantProps['variant']
|
||||
size?: ButtonVariantProps['size']
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
|
||||
export { Button, buttonVariants }
|
||||
25
v0-agent-frontend/v0-agent-frontend/components/ui/input.tsx
Normal file
25
v0-agent-frontend/v0-agent-frontend/components/ui/input.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-neutral-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-neutral-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-neutral-800 dark:bg-neutral-950 dark:ring-offset-neutral-950 dark:placeholder:text-neutral-400 dark:focus-visible:ring-neutral-300",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Input.displayName = "Input"
|
||||
|
||||
export { Input }
|
||||
@@ -0,0 +1,48 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none select-none transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-neutral-200 dark:bg-neutral-800" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
6
v0-agent-frontend/v0-agent-frontend/lib/utils.ts
Normal file
6
v0-agent-frontend/v0-agent-frontend/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
4
v0-agent-frontend/v0-agent-frontend/next.config.mjs
Normal file
4
v0-agent-frontend/v0-agent-frontend/next.config.mjs
Normal file
@@ -0,0 +1,4 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
|
||||
export default nextConfig;
|
||||
6403
v0-agent-frontend/v0-agent-frontend/package-lock.json
generated
Normal file
6403
v0-agent-frontend/v0-agent-frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
v0-agent-frontend/v0-agent-frontend/package.json
Normal file
38
v0-agent-frontend/v0-agent-frontend/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "v0-agent-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@supabase/auth-helpers-nextjs": "^0.10.0",
|
||||
"@supabase/auth-ui-react": "^0.4.7",
|
||||
"@supabase/auth-ui-shared": "^0.1.8",
|
||||
"@supabase/supabase-js": "^2.45.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.439.0",
|
||||
"next": "14.2.8",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-markdown": "^9.0.1",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.8",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
8
v0-agent-frontend/v0-agent-frontend/postcss.config.mjs
Normal file
8
v0-agent-frontend/v0-agent-frontend/postcss.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
25
v0-agent-frontend/v0-agent-frontend/tailwind.config.ts
Normal file
25
v0-agent-frontend/v0-agent-frontend/tailwind.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: 'var(--background)',
|
||||
foreground: 'var(--foreground)'
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
||||
export default config;
|
||||
26
v0-agent-frontend/v0-agent-frontend/tsconfig.json
Normal file
26
v0-agent-frontend/v0-agent-frontend/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user