diff --git a/Verba/.env.example b/Verba/.env.example deleted file mode 100644 index 0c8199e8..00000000 --- a/Verba/.env.example +++ /dev/null @@ -1,9 +0,0 @@ -#Audio transcription service - for multimodal preprocessing only -ASSEMBLYAI_API_KEY = your-key-here - -#Vector and generative services -OPENAI_API_KEY = your-key-here - -# Weaviate cloud deployment -WEAVIATE_URL_VERBA = shared-url-here -WEAVIATE_API_KEY_VERBA = shared-key-here diff --git a/Verba/Dockerfile b/Verba/Dockerfile deleted file mode 100644 index ceb7ea19..00000000 --- a/Verba/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM python:3.11 -WORKDIR /Verba -COPY . /Verba -RUN pip install -e '.' -EXPOSE 8000 -CMD ["verba", "start","--port","8000","--host","0.0.0.0"] diff --git a/Verba/FRONTEND.md b/Verba/FRONTEND.md deleted file mode 100644 index 29d6ce24..00000000 --- a/Verba/FRONTEND.md +++ /dev/null @@ -1,29 +0,0 @@ -# Verba - Frontend Documentation - -Verba's Frontend is a [NextJS](https://nextjs.org/) application used together with [TailwindCSS](https://tailwindcss.com/) and [DaisyUI](https://daisyui.com/). - -## 🚀 Setting Up the Frontend - -To get your local copy of the MinuteMate-Verba frontend up and running, please follow these simple steps: - -1. **Node.js Requirement**: - - - Confirm that Node.js version `>=21.3.0` is installed on your system. If you need to install or update Node.js, visit the official [Node.js website](https://nodejs.org/). - -2. **Installation**: - - - Navigate to the frontend directory: `cd frontend` - - Run `npm install` to install the dependencies required for the project. - -3. **Development Server**: - - Launch the application in development mode by executing `npm run dev`. - - Open your web browser and visit `http://localhost:3000` to view the application. - -4. **Clone Repository** - -## đŸ“Ļ Building Static Pages for FastAPI - -If you wish to serve and update the frontend through FastAPI, you need to build static pages: - -1. **Build Process**: - - Execute `npm run build` to generate the static production build. The output will be directed to the FastAPI folder configured to serve the static content. diff --git a/Verba/LICENSE b/Verba/LICENSE deleted file mode 100644 index 90b8b9b5..00000000 --- a/Verba/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2020-2023, Weaviate B.V. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Verba/MANIFEST.in b/Verba/MANIFEST.in deleted file mode 100644 index a9d03ffd..00000000 --- a/Verba/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -recursive-include goldenverba/server/frontend/out * \ No newline at end of file diff --git a/Verba/README.md b/Verba/README.md deleted file mode 100644 index 8df56784..00000000 --- a/Verba/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# Overview of MinuteMate-Verba - -Verba provided a framework for a RAG application, and we have modified this to create MinuteMate. For detailed information about Verba, see the [Verba](https://github.com/weaviate/Verba/) repo. - -# Deploying MinuteMate-Verba - -## Local Docker Deployment -*See Verba docs for other deployment options* - -1. Preparation: -- Install Docker Desktop -- Make sure Docker Desktop is running -- Clone the project repository - -2. Set up required external components and obtain any necessary URLs and API keys. See the **External Dependencies** section below for more details. -- Ingestion: If you're importing files in .PDF, .CSV/.XLSX, .DOCX, or other text-based formats, there are no external dependencies. If you're preprocessing audio data, you will need a transcription service, and AssemblyAI is currently the only integration available. For other ingestion options such as web crawling and unstructured data, see the Verba documentation. -- Vector database: always required. Weaviate is currently the only option, but can be deployed in a few ways. This document covers only the Weaviate Cloud option. -- Embedding model service: always required. This must be the same as the embedding model used to populate the vector database. -- Generative model service: always required. This can be whichever chat model or service you prefer. - -3. Place all required URLs and API keys in a .env file in the Verba folder. You can base this on '.env.example'; simply rename the file to .env and add relevant variables. These variables are passed along to the Docker container environment as specified in `docker-compose.yml`. So if you need to add variables to that environment, you must add them in both your .env file and in 'docker-compose.yml'. - -> Please make sure to only add environment variables that you really need. - -4. From the Verba folder, run the following command to create a Docker images, create a container, and start Verba within that container: - -```bash -docker compose --env-file .env up -d --build -``` - -5. Access Verba interface via web browser: -- You can access the Verba frontend at `localhost:8000` - -6. Select deployment type -- Select **🌩ī¸ Weaviate Cloud Deployment** - using a Weaviate instance that is hosted on Weaviate Cloud Services (WCS). [Weaviate Cluster Setup Guide](https://weaviate.io/developers/wcs/guides/create-instance) -- For other deployment options, see the Verba documentation. - -# External Dependencies - -## Preprocessing Audio Data - -### AssemblyAI - -[AssemblyAI](https://assemblyai.com/) provides transcription services. - -## Vector Database - -### Weaviate - -You can read more about the Weaviate configuration in our [docker-compose documentation](https://weaviate.io/developers/weaviate/installation/docker-compose) - -## Embedding and Generative Models - -**Embedding Model Integrations**: Weaviate, Ollama, SentenceTransformers, Cohere, VoyageAI, OpenAI - -**Chat Model Integrations**: Ollama, Huggingface, Cohere, Anthropic, OpenAI, Groq - -### Deploying Llama on Modal - -See the [Llama_On_Modal](/Llama_On_Modal/README.md) component of this project. - -### Deploying Local Models w/ Ollama - -Verba supports Ollama models. Download and Install Ollama on your device (https://ollama.com/download). Make sure to install/run your preferred LLM using `ollama run `. - -Tested with `llama3`, `llama3:70b` and `mistral`. The bigger models generally perform better, but need more computational power. - -> Make sure Ollama Server runs in the background and that you don't ingest documents with different ollama models since their vector dimension can vary that will lead to errors - -You can verify that by running the following command - -``` -ollama run llama3 -``` \ No newline at end of file diff --git a/Verba/docker-compose.yml b/Verba/docker-compose.yml deleted file mode 100644 index d77641d7..00000000 --- a/Verba/docker-compose.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- - -services: - verba: - build: - context: ./ - dockerfile: Dockerfile - ports: - - 8000:8000 - environment: - #See .env.example or the Verba docs for more information about environmental variables - - # Audio transcription service - for multimodal only - otherwise comment out - - ASSEMBLYAI_API_KEY = $ASSEMBLYAI_API_KEY - - # Vector database - API key needed only for cloud deployments - - WEAVIATE_URL_VERBA = $WEAVIATE_URL_VERBA - - WEAVIATE_API_KEY_VERBA = $WEAVIATE_API_KEY_VERBA - - # Using OpenAI for embeddings and generation - for dev purposes only - - OPENAI_API_KEY = $OPENAI_API_KEY - - healthcheck: - test: wget --no-verbose --tries=3 --spider http://localhost:8000 || exit 1 - interval: 5s - timeout: 10s - retries: 5 - start_period: 10s \ No newline at end of file diff --git a/Verba/frontend/.eslintrc.json b/Verba/frontend/.eslintrc.json deleted file mode 100644 index bffb357a..00000000 --- a/Verba/frontend/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/Verba/frontend/app/api.ts b/Verba/frontend/app/api.ts deleted file mode 100644 index bf221272..00000000 --- a/Verba/frontend/app/api.ts +++ /dev/null @@ -1,657 +0,0 @@ -import { - ConnectPayload, - HealthPayload, - RAGConfig, - QueryPayload, - Credentials, - DocumentsPreviewPayload, - DocumentPayload, - ChunkScore, - ContentPayload, - ChunksPayload, - RAGConfigResponse, - AllSuggestionsPayload, - MetadataPayload, - DatacountResponse, - SuggestionsPayload, - ChunkPayload, - DocumentFilter, - VectorsPayload, - UserConfigResponse, - ThemeConfigResponse, - Theme, - UserConfig, - LabelsResponse, - Themes, -} from "./types"; - -const checkUrl = async (url: string): Promise => { - try { - const response = await fetch(url); - return response.ok; - } catch (error) { - console.error(`Failed to fetch from ${url}:`, error); - return false; - } -}; - -export const detectHost = async (): Promise => { - const localUrl = "http://localhost:8000/api/health"; - const rootUrl = "/api/health"; - - const isLocalHealthy = await checkUrl(localUrl); - if (isLocalHealthy) { - return "http://localhost:8000"; - } - - const isRootHealthy = await checkUrl(rootUrl); - if (isRootHealthy) { - const root = window.location.origin; - return root; - } - - throw new Error("Both health checks failed, please check the Verba Server"); -}; - -export const fetchData = async (endpoint: string): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}${endpoint}`, { method: "GET" }); - const data = await response.json(); - - if (!data) { - console.warn(`Could not retrieve data from ${endpoint}`); - } - - return data; - } catch (error) { - console.error(`Failed to fetch data from ${endpoint}:`, error); - return null; - } -}; - -// Endpoint /api/health -export const fetchHealth = (): Promise => - fetchData("/api/health"); - -// Endpoint /api/connect -export const connectToVerba = async ( - deployment: string, - url: string, - apiKey: string, - port: string -): Promise => { - const host = await detectHost(); - const response = await fetch(`${host}/api/connect`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - credentials: { - deployment: deployment, - url: url, - key: apiKey, - }, - port: port, - }), - }); - const data = await response.json(); - return data; -}; - -// Endpoint /api/get_rag_config -export const fetchRAGConfig = async ( - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_rag_config`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - }); - const data: RAGConfigResponse = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/set_rag_config -export const updateRAGConfig = async ( - RAG: RAGConfig | null, - credentials: Credentials -): Promise => { - if (!RAG) { - return false; - } - - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/set_rag_config`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ rag_config: RAG, credentials: credentials }), - }); - - return response.status === 200; - } catch (error) { - console.error("Error setting config:", error); - return false; - } -}; - -// Endpoint /api/get_user_config -export const fetchUserConfig = async ( - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_user_config`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - }); - const data: UserConfigResponse = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/set_user_config -export const updateUserConfig = async ( - user_config: UserConfig, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/set_user_config`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - user_config: user_config, - credentials: credentials, - }), - }); - - return response.status === 200; - } catch (error) { - console.error("Error setting config:", error); - return false; - } -}; - -// Endpoint /api/get_theme_config -export const fetchThemeConfig = async ( - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_theme_config`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - }); - const data: ThemeConfigResponse = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/set_theme_config -export const updateThemeConfig = async ( - themes: Themes, - theme: Theme, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/set_theme_config`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - themes: themes, - theme: theme, - credentials: credentials, - }), - }); - - return response.status === 200; - } catch (error) { - console.error("Error setting config:", error); - return false; - } -}; - -// Endpoint /api/query -export const sendUserQuery = async ( - query: string, - RAG: RAGConfig | null, - labels: string[], - documentFilter: DocumentFilter[], - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/query`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - query: query, - RAG: RAG, - labels: labels, - documentFilter: documentFilter, - credentials: credentials, - }), - }); - - const data: QueryPayload = await response.json(); - return data; - } catch (error) { - console.error("Error sending query", error); - return null; - } -}; - -// Endpoint /api/get_document -export const fetchSelectedDocument = async ( - uuid: string | null, - credentials: Credentials -): Promise => { - if (!uuid) { - return null; - } - - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_document`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - credentials: credentials, - }), - }); - const data: DocumentPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving selected document", error); - return null; - } -}; - -// Endpoint /api/get_datacount -export const fetchDatacount = async ( - embedding_model: string, - documentFilter: DocumentFilter[], - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_datacount`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - embedding_model: embedding_model, - documentFilter: documentFilter, - credentials: credentials, - }), - }); - const data: DatacountResponse = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/get_labels -export const fetchLabels = async ( - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_labels`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - }); - const data: LabelsResponse = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/get_content -export const fetchContent = async ( - uuid: string | null, - page: number, - chunkScores: ChunkScore[], - credentials: Credentials -): Promise => { - if (!uuid) { - return null; - } - - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_content`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - page: page, - chunkScores: chunkScores, - credentials: credentials, - }), - }); - const data: ContentPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/get_vectors -export const fetch_vectors = async ( - uuid: string | null, - showAll: boolean, - credentials: Credentials -): Promise => { - if (!uuid) { - return null; - } - - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_vectors`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - showAll: showAll, - credentials: credentials, - }), - }); - const data: VectorsPayload | null = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/get_chunks -export const fetch_chunks = async ( - uuid: string | null, - page: number, - pageSize: number, - credentials: Credentials -): Promise => { - if (!uuid) { - return null; - } - - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_chunks`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - page: page, - pageSize: pageSize, - credentials: credentials, - }), - }); - const data: ChunksPayload | null = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/get_chunk -export const fetch_chunk = async ( - uuid: string | null, - embedder: string, - credentials: Credentials -): Promise => { - if (!uuid) { - return null; - } - - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_chunk`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - embedder: embedder, - credentials: credentials, - }), - }); - const data: ChunkPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving content", error); - return null; - } -}; - -// Endpoint /api/get_all_documents -export const retrieveAllDocuments = async ( - query: string, - labels: string[], - page: number, - pageSize: number, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_all_documents`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - query: query, - labels: labels, - page: page, - pageSize: pageSize, - credentials: credentials, - }), - }); - const data: DocumentsPreviewPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving all documents", error); - return null; - } -}; - -// Endpoint /api/delete_document -export const deleteDocument = async ( - uuid: string, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/delete_document`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - credentials: credentials, - }), - }); - return response.status === 200; - } catch (error) { - console.error("Error deleting document", error); - return false; - } -}; - -// Endpoint /api/reset -export const deleteAllDocuments = async ( - resetMode: string, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/reset`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - resetMode: resetMode, - credentials: credentials, - }), - }); - return response.status === 200; - } catch (error) { - console.error("Error deleting all documents", error); - return false; - } -}; - -// Endpoint /api/get_meta -export const fetchMeta = async ( - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_meta`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - }); - const data: MetadataPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving selected document", error); - return null; - } -}; - -// Endpoint /api/get_suggestions -export const fetchSuggestions = async ( - query: string, - limit: number, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_suggestions`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - query: query, - limit: limit, - credentials: credentials, - }), - }); - const data: SuggestionsPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving suggestions", error); - return null; - } -}; - -// Endpoint /api/delete_suggestion -export const deleteSuggestion = async ( - uuid: string, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/delete_suggestion`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - uuid: uuid, - credentials: credentials, - }), - }); - return response.status === 200; - } catch (error) { - console.error("Error deleting suggestion", error); - return false; - } -}; - -// Endpoint /api/get_all_suggestions -export const fetchAllSuggestions = async ( - page: number, - pageSize: number, - credentials: Credentials -): Promise => { - try { - const host = await detectHost(); - const response = await fetch(`${host}/api/get_all_suggestions`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - page: page, - pageSize: pageSize, - credentials: credentials, - }), - }); - const data: AllSuggestionsPayload = await response.json(); - return data; - } catch (error) { - console.error("Error retrieving all suggestions", error); - return null; - } -}; diff --git a/Verba/frontend/app/components/Chat/ChatConfig.tsx b/Verba/frontend/app/components/Chat/ChatConfig.tsx deleted file mode 100644 index 116900c0..00000000 --- a/Verba/frontend/app/components/Chat/ChatConfig.tsx +++ /dev/null @@ -1,146 +0,0 @@ -"use client"; - -import React, { useCallback } from "react"; -import { MdCancel } from "react-icons/md"; -import { IoSettingsSharp } from "react-icons/io5"; -import { RAGConfig, RAGComponentConfig, Credentials } from "@/app/types"; -import { updateRAGConfig } from "@/app/api"; -import ComponentView from "../Ingestion/ComponentView"; - -import VerbaButton from "../Navigation/VerbaButton"; - -interface ChatConfigProps { - RAGConfig: RAGConfig | null; - setRAGConfig: React.Dispatch>; - onSave: () => void; // New parameter for handling save - onReset: () => void; // New parameter for handling reset - addStatusMessage: ( - message: string, - type: "INFO" | "WARNING" | "SUCCESS" | "ERROR" - ) => void; - credentials: Credentials; - production: "Local" | "Demo" | "Production"; -} - -const ChatConfig: React.FC = ({ - RAGConfig, - setRAGConfig, - addStatusMessage, - onSave, - credentials, - onReset, - production, -}) => { - const updateConfig = ( - component_n: string, - configTitle: string, - value: string | boolean | string[] - ) => { - setRAGConfig((prevRAGConfig) => { - if (prevRAGConfig) { - const newRAGConfig = { ...prevRAGConfig }; - if (typeof value === "string" || typeof value === "boolean") { - newRAGConfig[component_n].components[ - newRAGConfig[component_n].selected - ].config[configTitle].value = value; - } else { - newRAGConfig[component_n].components[ - newRAGConfig[component_n].selected - ].config[configTitle].values = value; - } - return newRAGConfig; - } - return prevRAGConfig; - }); - }; - - const selectComponent = (component_n: string, selected_component: string) => { - setRAGConfig((prevRAGConfig) => { - if (prevRAGConfig) { - const newRAGConfig = { ...prevRAGConfig }; - newRAGConfig[component_n].selected = selected_component; - return newRAGConfig; - } - return prevRAGConfig; - }); - }; - - const saveComponentConfig = useCallback( - async ( - component_n: string, - selected_component: string, - component_config: RAGComponentConfig - ) => { - if (!RAGConfig) return; - - addStatusMessage("Saving " + selected_component + " Config", "SUCCESS"); - - const newRAGConfig = JSON.parse(JSON.stringify(RAGConfig)); - newRAGConfig[component_n].selected = selected_component; - newRAGConfig[component_n].components[selected_component] = - component_config; - const response = await updateRAGConfig(newRAGConfig, credentials); - if (response) { - setRAGConfig(newRAGConfig); - } - }, - [RAGConfig, credentials] - ); - - if (RAGConfig) { - return ( -
-
- {/* Add Save and Reset buttons */} -
- - -
-
- -
- - - -
-
- ); - } else { - return
; - } -}; - -export default ChatConfig; diff --git a/Verba/frontend/app/components/Chat/ChatInterface.tsx b/Verba/frontend/app/components/Chat/ChatInterface.tsx deleted file mode 100644 index b4aeaccb..00000000 --- a/Verba/frontend/app/components/Chat/ChatInterface.tsx +++ /dev/null @@ -1,691 +0,0 @@ -"use client"; - -import React, { useState, useEffect, useRef } from "react"; -import { MdCancel, MdOutlineRefresh } from "react-icons/md"; -import { TbPlugConnected } from "react-icons/tb"; -import { IoChatbubbleSharp } from "react-icons/io5"; -import { FaHammer } from "react-icons/fa"; -import { IoIosSend } from "react-icons/io"; -import { BiError } from "react-icons/bi"; -import { IoMdAddCircle } from "react-icons/io"; -import VerbaButton from "../Navigation/VerbaButton"; - -import { - updateRAGConfig, - sendUserQuery, - fetchDatacount, - fetchRAGConfig, - fetchSuggestions, - fetchLabels, -} from "@/app/api"; -import { getWebSocketApiHost } from "@/app/util"; -import { - Credentials, - QueryPayload, - Suggestion, - DataCountPayload, - ChunkScore, - Message, - LabelsResponse, - RAGConfig, - Theme, - DocumentFilter, -} from "@/app/types"; - -import InfoComponent from "../Navigation/InfoComponent"; -import ChatConfig from "./ChatConfig"; -import ChatMessage from "./ChatMessage"; - -interface ChatInterfaceProps { - credentials: Credentials; - setSelectedDocument: (s: string | null) => void; - setSelectedChunkScore: (c: ChunkScore[]) => void; - currentPage: string; - RAGConfig: RAGConfig | null; - setRAGConfig: React.Dispatch>; - selectedTheme: Theme; - production: "Local" | "Demo" | "Production"; - addStatusMessage: ( - message: string, - type: "INFO" | "WARNING" | "SUCCESS" | "ERROR" - ) => void; - documentFilter: DocumentFilter[]; - setDocumentFilter: React.Dispatch>; -} - -const ChatInterface: React.FC = ({ - production, - credentials, - setSelectedDocument, - setSelectedChunkScore, - currentPage, - RAGConfig, - selectedTheme, - setRAGConfig, - addStatusMessage, - documentFilter, - setDocumentFilter, -}) => { - const [selectedSetting, setSelectedSetting] = useState("Chat"); - - const isFetching = useRef(false); - const [fetchingStatus, setFetchingStatus] = useState< - "DONE" | "CHUNKS" | "RESPONSE" - >("DONE"); - - const [previewText, setPreviewText] = useState(""); - const lastMessageRef = useRef(null); - const [socket, setSocket] = useState(null); - const [socketOnline, setSocketOnline] = useState(false); - const [reconnect, setReconnect] = useState(false); - - const [currentSuggestions, setCurrentSuggestions] = useState( - [] - ); - - const [labels, setLabels] = useState([]); - const [filterLabels, setFilterLabels] = useState([]); - - const [selectedDocumentScore, setSelectedDocumentScore] = useState< - string | null - >(null); - - const [currentDatacount, setCurrentDatacount] = useState(0); - - const [userInput, setUserInput] = useState(""); - const [messages, setMessages] = useState([]); - const [isComposing, setIsComposing] = useState(false); - - const currentEmbedding = RAGConfig - ? (RAGConfig["Embedder"].components[RAGConfig["Embedder"].selected].config[ - "Model" - ].value as string) - : "No Config found"; - useState("No Embedding Model"); - - useEffect(() => { - setReconnect(true); - }, []); - - useEffect(() => { - if (RAGConfig) { - retrieveDatacount(); - } else { - setCurrentDatacount(0); - } - }, [currentEmbedding, currentPage, documentFilter]); - - useEffect(() => { - setMessages((prev) => { - if (prev.length === 0) { - return [ - { - type: "system", - content: selectedTheme.intro_message.text, - }, - ]; - } - return prev; - }); - }, [selectedTheme.intro_message.text]); - - // Setup WebSocket and messages to /ws/generate_stream - useEffect(() => { - const socketHost = getWebSocketApiHost(); - const localSocket = new WebSocket(socketHost); - - localSocket.onopen = () => { - console.log("WebSocket connection opened to " + socketHost); - setSocketOnline(true); - }; - - localSocket.onmessage = (event) => { - let data; - - if (!isFetching.current) { - setPreviewText(""); - return; - } - - try { - data = JSON.parse(event.data); - } catch (e) { - console.error("Received data is not valid JSON:", event.data); - return; // Exit early if data isn't valid JSON - } - - const newMessageContent = data.message; - setPreviewText((prev) => prev + newMessageContent); - - if (data.finish_reason === "stop") { - isFetching.current = false; - setFetchingStatus("DONE"); - addStatusMessage("Finished generation", "SUCCESS"); - const full_text = data.full_text; - if (data.cached) { - const distance = data.distance; - setMessages((prev) => [ - ...prev, - { - type: "system", - content: full_text, - cached: true, - distance: distance, - }, - ]); - } else { - setMessages((prev) => [ - ...prev, - { type: "system", content: full_text }, - ]); - } - setPreviewText(""); - } - }; - - localSocket.onerror = (error) => { - console.error("WebSocket Error:", error); - setSocketOnline(false); - isFetching.current = false; - setFetchingStatus("DONE"); - setReconnect((prev) => !prev); - }; - - localSocket.onclose = (event) => { - if (event.wasClean) { - console.log( - `WebSocket connection closed cleanly, code=${event.code}, reason=${event.reason}` - ); - } else { - console.error("WebSocket connection died"); - } - setSocketOnline(false); - isFetching.current = false; - setFetchingStatus("DONE"); - setReconnect((prev) => !prev); - }; - - setSocket(localSocket); - - return () => { - if (localSocket.readyState !== WebSocket.CLOSED) { - localSocket.close(); - } - }; - }, [reconnect]); - - useEffect(() => { - if (RAGConfig) { - retrieveDatacount(); - } else { - setCurrentDatacount(0); - } - }, [RAGConfig]); - - const retrieveRAGConfig = async () => { - const config = await fetchRAGConfig(credentials); - if (config) { - setRAGConfig(config.rag_config); - } else { - addStatusMessage("Failed to fetch RAG Config", "ERROR"); - } - }; - - const sendUserMessage = async () => { - if (isFetching.current || !userInput.trim()) return; - - const sendInput = userInput; - setUserInput(""); - isFetching.current = true; - setCurrentSuggestions([]); - setFetchingStatus("CHUNKS"); - setMessages((prev) => [...prev, { type: "user", content: sendInput }]); - - try { - addStatusMessage("Sending query...", "INFO"); - const data = await sendUserQuery( - sendInput, - RAGConfig, - filterLabels, - documentFilter, - credentials - ); - - if (!data || data.error) { - handleErrorResponse(data ? data.error : "No data received"); - } else { - handleSuccessResponse(data, sendInput); - } - } catch (error) { - handleErrorResponse("Failed to fetch from API"); - console.error("Failed to fetch from API:", error); - } - }; - - const handleErrorResponse = (errorMessage: string) => { - addStatusMessage("Query failed", "ERROR"); - setMessages((prev) => [...prev, { type: "error", content: errorMessage }]); - isFetching.current = false; - setFetchingStatus("DONE"); - }; - - const handleSuccessResponse = (data: QueryPayload, sendInput: string) => { - setMessages((prev) => [ - ...prev, - { type: "retrieval", content: data.documents, context: data.context }, - ]); - - addStatusMessage( - "Received " + Object.entries(data.documents).length + " documents", - "SUCCESS" - ); - - if (data.documents.length > 0) { - const firstDoc = data.documents[0]; - setSelectedDocument(firstDoc.uuid); - setSelectedDocumentScore( - `${firstDoc.uuid}${firstDoc.score}${firstDoc.chunks.length}` - ); - setSelectedChunkScore(firstDoc.chunks); - - if (data.context) { - streamResponses(sendInput, data.context); - setFetchingStatus("RESPONSE"); - } - } else { - handleErrorResponse("We couldn't find any chunks to your query"); - } - }; - - const streamResponses = (query?: string, context?: string) => { - if (socket?.readyState === WebSocket.OPEN) { - const filteredMessages = messages - .slice(1) // Skip the first message - .filter((msg) => msg.type === "user" || msg.type === "system") - .map((msg) => ({ - type: msg.type, - content: msg.content, - })); - - const data = JSON.stringify({ - query: query, - context: context, - conversation: filteredMessages, - rag_config: RAGConfig, - }); - socket.send(data); - } else { - console.error("WebSocket is not open. ReadyState:", socket?.readyState); - } - }; - - const handleCompositionStart = () => { - setIsComposing(true); - } - - const handleCompositionEnd = () => { - setIsComposing(false); - } - - const handleKeyDown = (e: any) => { - if (e.key === "Enter" && !e.shiftKey && !isComposing) { - e.preventDefault(); // Prevent new line - sendUserMessage(); // Submit form - } - }; - - const retrieveDatacount = async () => { - try { - const data: DataCountPayload | null = await fetchDatacount( - currentEmbedding, - documentFilter, - credentials - ); - const labels: LabelsResponse | null = await fetchLabels(credentials); - if (data) { - setCurrentDatacount(data.datacount); - } - if (labels) { - setLabels(labels.labels); - } - } catch (error) { - console.error("Failed to fetch from API:", error); - addStatusMessage("Failed to fetch datacount: " + error, "ERROR"); - } - }; - - const reconnectToVerba = () => { - setReconnect((prevState) => !prevState); - }; - - const onSaveConfig = async () => { - addStatusMessage("Saved Config", "SUCCESS"); - await updateRAGConfig(RAGConfig, credentials); - }; - - const onResetConfig = async () => { - addStatusMessage("Reset Config", "WARNING"); - retrieveRAGConfig(); - }; - - const handleSuggestions = async () => { - if ( - RAGConfig && - RAGConfig["Retriever"].components[RAGConfig["Retriever"].selected].config[ - "Suggestion" - ].value - ) { - const suggestions = await fetchSuggestions(userInput, 3, credentials); - if (suggestions) { - setCurrentSuggestions(suggestions.suggestions); - } - } - }; - - return ( -
- {/* Header */} -
-
- -
-
- { - setSelectedSetting("Chat"); - }} - selected={selectedSetting === "Chat"} - disabled={false} - selected_color="bg-secondary-verba" - /> - {production != "Demo" && ( - { - setSelectedSetting("Config"); - }} - selected={selectedSetting === "Config"} - disabled={false} - selected_color="bg-secondary-verba" - /> - )} -
-
- -
- {/* New fixed tab */} - {selectedSetting == "Chat" && ( -
-
- - {(filterLabels.length > 0 || documentFilter.length > 0) && ( - { - setFilterLabels([]); - setDocumentFilter([]); - }} - title="Clear" - className="btn-sm max-w-min" - icon_size={12} - text_class_name="text-xs" - Icon={MdCancel} - selected={false} - disabled={false} - /> - )} -
-
- {filterLabels.map((label, index) => ( - { - setFilterLabels(filterLabels.filter((l) => l !== label)); - }} - /> - ))} - {documentFilter.map((filter, index) => ( - { - setDocumentFilter( - documentFilter.filter((f) => f.uuid !== filter.uuid) - ); - }} - /> - ))} -
-
- )} -
-
- {currentDatacount === 0 && } - {currentDatacount === 0 && ( -

{`${currentDatacount} documents embedded by ${currentEmbedding}`}

- )} -
- {messages.map((message, index) => ( -
- -
- ))} - {previewText && ( - - )} - {isFetching.current && ( -
-
- -

- {fetchingStatus === "CHUNKS" && "Retrieving..."} - {fetchingStatus === "RESPONSE" && "Generating..."} -

- -
-
- )} -
- {selectedSetting === "Config" && ( - - )} -
- -
- {socketOnline ? ( -
-
-