From 91353bd051f6235ef8eed655897591c76eaa06bd Mon Sep 17 00:00:00 2001 From: sageTheDM Date: Fri, 11 Oct 2024 10:18:33 +0200 Subject: [PATCH] main (#137) Reviewed-on: https://interstellardevelopment.org/code/code/React-Group/interstellar_ai/pulls/137 Reviewed-by: Patrick Co-authored-by: sageTheDM Co-committed-by: sageTheDM --- app/backend/GetLocalStorage.ts | 23 +-- app/backend/InputOutputHandler.tsx | 17 ++- app/backend/database.ts | 73 ++++----- app/backend/threads/GetWorker.ts | 35 ++--- app/backend/threads/PostWorker.ts | 29 ++-- app/backend/voice_backend.ts | 26 ++-- app/backend/weather.ts | 26 ++-- app/components/AI.tsx | 3 +- app/components/Credits.tsx | 31 +++- app/components/Documentation.tsx | 3 +- app/components/Faq.tsx | 113 +++++--------- app/components/Header.tsx | 59 ++++---- app/components/History.tsx | 150 +++++++++++-------- app/components/Models.tsx | 9 +- app/components/settings/ButtonSettings.tsx | 8 +- app/components/settings/CheckBox.tsx | 15 +- app/components/settings/ColorSettings.tsx | 35 +++-- app/components/settings/DropDown.tsx | 48 +++--- app/components/settings/DropDownTheme.tsx | 25 ++-- app/components/settings/FontSize.tsx | 25 ++-- app/components/settings/OpenSourceToggle.tsx | 15 +- app/components/settings/PrivacySettings.tsx | 24 +-- app/components/settings/Settings.tsx | 16 +- app/components/settings/settingUtils.ts | 18 ++- app/components/settings/theme.ts | 10 +- app/hooks/useChatHistory.tsx | 96 ++++++++---- app/layout.tsx | 19 ++- app/page.tsx | 43 ++++-- py/ai.py | 35 +++-- py/api.py | 122 ++++++++------- py/db.py | 82 +++++----- py/tts.py | 1 - py/voice.py | 14 +- py/weather.py | 1 + 34 files changed, 682 insertions(+), 567 deletions(-) diff --git a/app/backend/GetLocalStorage.ts b/app/backend/GetLocalStorage.ts index e838f74..487413f 100644 --- a/app/backend/GetLocalStorage.ts +++ b/app/backend/GetLocalStorage.ts @@ -1,16 +1,19 @@ // getLocalStorageData.ts +// Function to retrieve all items from localStorage export const getAllLocalStorageItems = (): Record => { - const allData: Record = {}; + const allData: Record = {}; // Object to hold key-value pairs from localStorage + + // Check if localStorage is available if (typeof localStorage !== 'undefined') { - for (let i = 0; i < localStorage.length; i++) { - const key = localStorage.key(i); - if (key) { - const value = localStorage.getItem(key); - allData[key] = value; + // Iterate through all localStorage keys + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); // Get the key at the current index + if (key) { + const value = localStorage.getItem(key); // Retrieve the value associated with the key + allData[key] = value; // Store the key-value pair in the allData object + } } - } } - return allData; - }; - \ No newline at end of file + return allData; // Return the object containing all localStorage items +}; diff --git a/app/backend/InputOutputHandler.tsx b/app/backend/InputOutputHandler.tsx index bcdd034..fc9b9d6 100644 --- a/app/backend/InputOutputHandler.tsx +++ b/app/backend/InputOutputHandler.tsx @@ -9,7 +9,7 @@ import { getWeather } from "./weather"; import { changeHistory, getHistory } from "./database"; const InputOutputBackend: React.FC = () => { - // # variables + //#region variables type Message = { role: string content: string @@ -37,7 +37,7 @@ const InputOutputBackend: React.FC = () => { } console.log(setSelectedIndex) - + //#region useEffect useEffect(() => { setMessages(chatHistory.chats[chatHistory.selectedIndex].messages) }, [chatHistory.selectedIndex]) @@ -86,6 +86,7 @@ const InputOutputBackend: React.FC = () => { } }, [chatHistory]) + //#region functions const getWeatherHere = async () => { setWeatherData(await getWeather({ "unit_type": preferredMeasurement, "city": localStorage.getItem("weatherInfo") || "New York" })) console.log("Got the Data!") @@ -104,7 +105,7 @@ const InputOutputBackend: React.FC = () => { } setChatHistoryTriggered(true) } - + //#region system-prompt useEffect(() => { console.log("creating system prompt") console.log(weatherData) @@ -133,7 +134,7 @@ const InputOutputBackend: React.FC = () => { console.log(messages) }, [systemMessage]) - + //#region more variables and functions const conversationRef = useRef(null) const [copyClicked, setCopyClicked] = useState(false) const [accessToken, setAccessToken] = useState("") @@ -144,7 +145,7 @@ const InputOutputBackend: React.FC = () => { const [isRecording, setIsRecording] = useState(false) const mediaRecorderRef = useRef(null) const audioChunks = useRef([]) - + //#region chat functions useEffect(() => { getNewToken() @@ -266,7 +267,7 @@ const InputOutputBackend: React.FC = () => { } } } - + //#region speech recognition const startRecording = async (): Promise => { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const mediaRecorder = new MediaRecorder(stream); @@ -309,7 +310,7 @@ const InputOutputBackend: React.FC = () => { stopRecording(); } }; - + //#region chat buttons const handleStopClick = () => { endGetWorker() getNewToken() @@ -356,7 +357,7 @@ const InputOutputBackend: React.FC = () => { setCopyClicked(false) } - + //#region The "html" return return ( <> contains the action you want to do, there are: create_account, change_password, get_data, change_data, check_credentials, delete_account -username -> contains the current username, required for create_account, but can be omitted in favor of email in other requests. Preffered over email authentication. -email -> contains the current email, required for create_account, but just like the username, it can be omitted, in favor of the other, sending both is possible too. -password -> contains the password, required for all requests. -new_password -> in the case you are changing your password, you will need to use this in addition to password, to specify the new password. -data -> data contains all the data you want to store, you have to always give the entire data, because the data you give here overwrites the data in the database, -so if you only give the chat history for example, all settings will be deleted, and if you only give settings, all chat histories will get deleted. - - -if all went well, you will get the status 200 in response.data.status -to check if the request was accepted or declined, check response.data.response, it will be either true or false depending on if it worked, or not. -*/ - -const apiURL = new URL("http://localhost:5000/interstellar_ai/db") +// Construct the base API URL based on the environment +const apiURL = new URL("http://localhost:5000/interstellar_ai/db"); if (typeof window !== 'undefined') { - apiURL.hostname = window.location.hostname; + apiURL.hostname = window.location.hostname; // Set hostname for browsers } else { - apiURL.hostname = "localhost" + apiURL.hostname = "localhost"; // Default to localhost for non-browser environments } +// Function to send data to the database and return a success status export const sendToDatabase = async (data: object): Promise => { try { const response = await axios.post(apiURL.href, data); const status = response.data.status; const success = response.data.response; - postMessage({ status, success }); - return success; + postMessage({ status, success }); // Send status back to the main thread + return success; // Return success status } catch (error) { - postMessage({ status: 500, success: false }); - console.log(error) - return false; + postMessage({ status: 500, success: false }); // Handle errors + console.log(error); + return false; // Return false on error } }; +// Function to send data and get a string response export const sendToDatabaseAndGetString = async (data: object): Promise => { try { const response = await axios.post(apiURL.href, data); const status = response.data.status; const success = response.data.response; postMessage({ status, success }); - return success; + return success; // Return response string } catch (error) { postMessage({ status: 500, success: false }); - console.log(error) - return "false"; + console.log(error); + return "false"; // Return "false" on error } }; @@ -61,7 +46,7 @@ export const createAccount = async (username: string, email: string, password: s email: email, password: password, }; - return await sendToDatabase(data); + return await sendToDatabase(data); // Send account creation request }; export const changePassword = async (usernameOrEmail: string, password: string, newPassword: string) => { @@ -72,7 +57,7 @@ export const changePassword = async (usernameOrEmail: string, password: string, password, new_password: newPassword, }; - return await sendToDatabase(data); + return await sendToDatabase(data); // Send password change request }; export const getSettings = async (usernameOrEmail: string, password: string) => { @@ -82,7 +67,7 @@ export const getSettings = async (usernameOrEmail: string, password: string) => email: usernameOrEmail.includes('@') ? usernameOrEmail : undefined, password, }; - return await sendToDatabaseAndGetString(data); + return await sendToDatabaseAndGetString(data); // Get user settings }; export const changeSettings = async (usernameOrEmail: string, password: string, newData: object) => { @@ -93,7 +78,7 @@ export const changeSettings = async (usernameOrEmail: string, password: string, password, data: newData, }; - return await sendToDatabase(data); + return await sendToDatabase(data); // Send settings change request }; export const getHistory = async (usernameOrEmail: string, password: string) => { @@ -103,7 +88,7 @@ export const getHistory = async (usernameOrEmail: string, password: string) => { email: usernameOrEmail.includes('@') ? usernameOrEmail : undefined, password, }; - return await sendToDatabaseAndGetString(data); + return await sendToDatabaseAndGetString(data); // Get user history }; export const changeHistory = async (usernameOrEmail: string, password: string, newData: object) => { @@ -114,7 +99,7 @@ export const changeHistory = async (usernameOrEmail: string, password: string, n password, data: newData, }; - return await sendToDatabase(data); + return await sendToDatabase(data); // Send history change request }; export const getEmail = async (usernameOrEmail: string, password: string): Promise => { @@ -124,7 +109,7 @@ export const getEmail = async (usernameOrEmail: string, password: string): Promi email: usernameOrEmail.includes('@') ? usernameOrEmail : undefined, password, }; - return await sendToDatabaseAndGetString(data); + return await sendToDatabaseAndGetString(data); // Get user email }; export const getName = async (usernameOrEmail: string, password: string): Promise => { @@ -134,10 +119,9 @@ export const getName = async (usernameOrEmail: string, password: string): Promis email: usernameOrEmail.includes('@') ? usernameOrEmail : undefined, password, }; - return await sendToDatabaseAndGetString(data); + return await sendToDatabaseAndGetString(data); // Get user name }; - export const checkCredentials = async (usernameOrEmail: string, password: string) => { const data = { action: "check_credentials", @@ -145,15 +129,16 @@ export const checkCredentials = async (usernameOrEmail: string, password: string email: usernameOrEmail.includes('@') ? usernameOrEmail : undefined, password, }; - const sendBack = await sendToDatabase(data); + const sendBack = await sendToDatabase(data); // Check user credentials if (sendBack) { if (typeof localStorage !== 'undefined') { - localStorage.setItem("accountEmail", await getEmail(usernameOrEmail, password)) - localStorage.setItem("accountName", await getName(usernameOrEmail, password)) - localStorage.setItem("accountPassword", password) + // Store user data in localStorage if credentials are valid + localStorage.setItem("accountEmail", await getEmail(usernameOrEmail, password)); + localStorage.setItem("accountName", await getName(usernameOrEmail, password)); + localStorage.setItem("accountPassword", password); } } - return sendBack + return sendBack; // Return success status }; export const deleteAccount = async (usernameOrEmail: string, password: string) => { @@ -163,5 +148,5 @@ export const deleteAccount = async (usernameOrEmail: string, password: string) = email: usernameOrEmail.includes('@') ? usernameOrEmail : undefined, password, }; - return await sendToDatabase(data); + return await sendToDatabase(data); // Send account deletion request }; diff --git a/app/backend/threads/GetWorker.ts b/app/backend/threads/GetWorker.ts index 2813a93..ae1cec3 100644 --- a/app/backend/threads/GetWorker.ts +++ b/app/backend/threads/GetWorker.ts @@ -1,37 +1,32 @@ import axios from "axios"; -let windownameGlobal = "" +let windownameGlobal = ""; // Global variable to hold the window name +let accesstoken = ""; // Variable to store the access token -let accesstoken = "" onmessage = (event) => { - const { action, access_token, windowname } = event.data - accesstoken = access_token - - windownameGlobal = windowname + const { action, access_token, windowname } = event.data; + accesstoken = access_token; + windownameGlobal = windowname; if (action === "start") { - fetchData() - } else if (action === "terminate") { + fetchData(); // Start fetching data on 'start' action } } - const fetchData = () => { + const apiURL = new URL("http://localhost:5000/interstellar_ai/api/ai_get?access_token=" + accesstoken); + apiURL.hostname = windownameGlobal; // Set the hostname - const apiURL = new URL("http://localhost:5000/interstellar_ai/api/ai_get?access_token=" + accesstoken) - apiURL.hostname = windownameGlobal; - - console.log(apiURL.href) + console.log(apiURL.href); // Log the constructed URL axios.get(apiURL.href) .then(response => { - const data = response.data - postMessage(data) - setTimeout(fetchData, 100) + postMessage(response.data); // Send data back on success + setTimeout(fetchData, 100); // Schedule next fetch }) .catch(error => { console.log('Error fetching data:', error); - postMessage({ error: "failed fetching data" }) - setTimeout(() => fetchData(), 1000) - }) -} \ No newline at end of file + postMessage({ error: "failed fetching data" }); // Send error message + setTimeout(() => fetchData(), 1000); // Retry after 1 second + }); +} diff --git a/app/backend/threads/PostWorker.ts b/app/backend/threads/PostWorker.ts index ed0a526..6e05d7d 100644 --- a/app/backend/threads/PostWorker.ts +++ b/app/backend/threads/PostWorker.ts @@ -1,31 +1,32 @@ import axios from "axios"; +// Event listener for incoming messages onmessage = (e) => { - const { messages, ai_model, model_type, access_token, api_key, windowname } = e.data - + const { messages, ai_model, model_type, access_token, api_key, windowname } = e.data; + // Construct the message object to send to the API const Message = { messages: messages, ai_model: ai_model, model_type: model_type, access_token: access_token, api_key: api_key - } + }; - const apiURL = new URL("http://localhost:5000/interstellar_ai/api/ai_send") - console.log(windowname) - apiURL.hostname = windowname; + const apiURL = new URL("http://localhost:5000/interstellar_ai/api/ai_send"); + console.log(windowname); // Log the window name + apiURL.hostname = windowname; // Set the hostname for the API request - console.log(apiURL.href) + console.log(apiURL.href); // Log the constructed API URL + // Make a POST request to the API with the message object axios.post(apiURL.href, Message) .then(response => { - const status = response.data.status - postMessage({ status }) - + const status = response.data.status; + postMessage({ status }); // Send the response status back }) .catch(error => { - console.log("Error calling API:", error) - postMessage({ status: 500 }) - }) -} \ No newline at end of file + console.log("Error calling API:", error); + postMessage({ status: 500 }); // Send error status if API call fails + }); +} diff --git a/app/backend/voice_backend.ts b/app/backend/voice_backend.ts index d950380..c4a9a09 100644 --- a/app/backend/voice_backend.ts +++ b/app/backend/voice_backend.ts @@ -1,24 +1,26 @@ import axios from "axios"; export const sendToVoiceRecognition = (audio_data: Blob): Promise => { + // Create a new FormData instance to send the audio file + const formdata = new FormData(); + formdata.append("audio", audio_data); // Append the audio data to the FormData - const formdata = new FormData() - formdata.append("audio", audio_data) - - const apiURL = new URL("http://localhost:5000/interstellar_ai/api/voice_recognition") + // Set the API URL dynamically based on the environment + const apiURL = new URL("http://localhost:5000/interstellar_ai/api/voice_recognition"); if (typeof window !== 'undefined') { - apiURL.hostname = window.location.hostname; + apiURL.hostname = window.location.hostname; // Use the current hostname in the browser } else { - apiURL.hostname = "localhost" + apiURL.hostname = "localhost"; // Fallback for server-side } + // Send the audio data to the API using POST request return axios.post(apiURL.href, formdata) .then((response) => { - return response.data.response + return response.data.response; // Return the response from the API }) .catch(error => { - console.log("Error calling API:", error) - postMessage({ status: 500 }) - return "Error" - }) -} \ No newline at end of file + console.log("Error calling API:", error); // Log any error that occurs + postMessage({ status: 500 }); // Indicate an error status to the worker + return "Error"; // Return a fallback error message + }); +}; diff --git a/app/backend/weather.ts b/app/backend/weather.ts index 66b861a..7ca070f 100644 --- a/app/backend/weather.ts +++ b/app/backend/weather.ts @@ -1,23 +1,29 @@ import axios from "axios"; -const apiURL = new URL("http://localhost:5000/interstellar_ai/api/weather") +// Initialize the API URL for the weather service +const apiURL = new URL("http://localhost:5000/interstellar_ai/api/weather"); if (typeof window !== 'undefined') { - apiURL.hostname = window.location.hostname; + apiURL.hostname = window.location.hostname; // Use current hostname in the browser } else { - apiURL.hostname = "localhost" + apiURL.hostname = "localhost"; // Fallback for server-side } +// Function to get weather data export const getWeather = async (data: object): Promise => { try { + // Make a POST request to the weather API with the provided data const response = await axios.post(apiURL.href, data); - const status = response.data.status; - const success = response.data.response; + const status = response.data.status; // Extract the status from the response + const success = response.data.response; // Extract the actual weather data from the response + + // Send the status and success response back to the worker postMessage({ status, success }); - console.log(JSON.stringify(success)) - return JSON.stringify(success); + console.log(JSON.stringify(success)); // Log the successful response for debugging + return JSON.stringify(success); // Return the weather data as a JSON string } catch (error) { + // Handle any errors that occur during the request postMessage({ status: 500, success: false }); - console.log(error) - return ""; + console.log(error); // Log the error for debugging + return ""; // Return an empty string in case of an error } -}; \ No newline at end of file +}; diff --git a/app/components/AI.tsx b/app/components/AI.tsx index 824f6d6..a226840 100644 --- a/app/components/AI.tsx +++ b/app/components/AI.tsx @@ -5,9 +5,10 @@ import InputOutputBackend from '../backend/InputOutputHandler'; const AI: React.FC = () => { return (
+ {/* Render the InputOutputBackend component for AI input/output handling */}
); }; -export default AI; \ No newline at end of file +export default AI; diff --git a/app/components/Credits.tsx b/app/components/Credits.tsx index 5c94887..0c09174 100644 --- a/app/components/Credits.tsx +++ b/app/components/Credits.tsx @@ -1,24 +1,45 @@ import React from 'react'; +// Main Credits Component const Credits: React.FC = () => (

Credits

+

Icons

This project utilizes the following icon resources:

- + +

Fonts

The fonts used in this project are provided by:

- - + +
); -const CreditLink = ({ href, label }: { href: string; label: string }) => ( - {label} +// CreditLink Component for rendering individual credit links +const CreditLink: React.FC<{ href: string; label: string }> = ({ href, label }) => ( + + {label} + ); export default Credits; + +// also thank you Leart and Tristan without you two we would not have come this far +// and a special thanks and why are you so annoying to Eslint \ No newline at end of file diff --git a/app/components/Documentation.tsx b/app/components/Documentation.tsx index 7e91235..69d6ad9 100644 --- a/app/components/Documentation.tsx +++ b/app/components/Documentation.tsx @@ -1,6 +1,7 @@ import React from 'react'; -const Documentation = () => { +// Documentation Component +const Documentation: React.FC = () => { return (
diff --git a/app/components/Faq.tsx b/app/components/Faq.tsx index 757bc9b..f59d7e8 100644 --- a/app/components/Faq.tsx +++ b/app/components/Faq.tsx @@ -1,107 +1,62 @@ import React from 'react'; +// FAQ Component const FAQ: React.FC = () => { return ( -
-

Frequently Asked Questions

- +
{/* Main section for FAQs */} +

Frequently Asked Questions

{/* Title for the FAQ section */} +
-

What is this AI assistant for?

-

This AI assistant helps you with various tasks such as answering questions, generating text, and even helping with code or writing tasks.

+

Why doesn't my selection in the category dropdown menu apply?

+

Currently, the dropdown menu for selecting AI models does not retain your choice after a website refresh.

- +
-

How does the AI assistant work?

-

The assistant uses machine learning algorithms to understand your input and provide contextually relevant answers or generate content based on the task you've described.

+

Why is the AI suddenly talking about the weather when I didn't select that option?

+

The AI is programmed to provide weather information even if you haven't specifically selected the weather option.

- +
-

Can I trust the answers given by the AI assistant?

-

While the AI strives to give accurate and helpful answers, it is important to verify critical information, especially for complex or sensitive queries.

+

Why isn't the online API working?

+

At the moment, the online APIs for Google and La Plateforme are not operational. However, the OpenAI and Anthropic APIs may still function.

- +
-

What kind of questions can I ask?

-

You can ask a wide range of questions from simple factual queries to more complex requests like generating creative writing or code snippets.

+

Why is the AI discussing unrelated topics?

+

Try disabling the AI system prompt settings, as the AI sometimes tends to focus on those topics excessively.

- +
-

Is my data secure when using the AI assistant?

-

We take privacy seriously. Your data is handled according to our privacy policy, ensuring that any personal information shared is securely processed and not misused.

+

Why isn't the AI responding in the format I specified in the settings?

+

Please check if the system prompt settings are enabled. If the issue persists, it may be because the AI is unable to fully adhere to the command.

- +
-

How can I provide feedback about the AI assistant?

-

Feedback can be provided through our feedback form, available on our website. We appreciate your input and use it to improve the AI assistant's performance.

+

Does this AI have the ability to know my location or search the web?

+

No, this AI does not possess any capabilities to access your location or browse the web.

- +
-

Can I customize the AI assistant's responses?

-

Customization options are limited in the current version, but we are working on features that will allow users to tailor responses to better suit their needs.

+

Does the AI really work offline?

+

Yes! Once you download the necessary models, it can operate fully offline, with the exception of the weather API.

- +
-

How frequently is the AI assistant updated?

-

The AI assistant is updated regularly to improve its functionality and incorporate new features. Stay tuned to our update logs for information on the latest improvements.

+

Are my messages encrypted?

+

Unfortunately, not at this time. We recommend keeping your messages as anonymous as possible.

- +
-

What should I do if the AI assistant is not working properly?

-

If you encounter any issues with the AI assistant, please contact our support team for assistance. Provide details about the problem to help us resolve it quickly.

+

Where is my data saved?

+

All data, including accounts, settings, and chats, is stored locally on your computer.

- +
-

Will the AI assistant be available in multiple languages?

-

Yes, the AI assistant is designed to support multiple languages. You can specify your language preference in the settings to receive responses in your chosen language.

+

Is this a virus?

+

No, this is not a virus. The warning appears because the application is not officially signed.

- -
-

How can I integrate the AI assistant into my own application?

-

Integration guidelines are available in our developer documentation. Follow the instructions to incorporate the AI assistant into your application via our API.

-
- -
-

Is there a mobile version of the AI assistant?

-

Currently, the AI assistant is optimized for desktop use. We are working on a mobile version to provide a seamless experience on smartphones and tablets.

-
- -
-

Can the AI assistant handle multiple simultaneous conversations?

-

Yes, the AI assistant is capable of managing multiple conversations at once, ensuring that each interaction is handled efficiently.

-
- -
-

What are the system requirements to use the AI assistant?

-

The AI assistant can be accessed through most modern web browsers. Ensure your browser is up-to-date for the best experience.

-
- -
-

How can I access previous conversations?

-

Previous conversations can be accessed through the chat history feature available in the assistant's interface.

-
- -
-

What are the limitations of the current AI assistant?

-

The AI assistant may have limitations in understanding highly specialized or nuanced topics. We are continuously working to expand its capabilities.

-
- -
-

How do I update my profile or settings?

-

Profile and settings updates can be made through the account management section of the application. Ensure you save your changes before exiting.

-
- -
-

Can the AI assistant be used offline?

-

Currently, the AI assistant requires an internet connection to function. Offline capabilities are being explored for future updates.

-
- -
-

Who can I contact for technical support?

-

Technical support can be reached through our support contact page on the website. Our team is available to help with any technical issues you may encounter.

-
-
); }; -export default FAQ; +export default FAQ; // Exporting the FAQ component diff --git a/app/components/Header.tsx b/app/components/Header.tsx index e477a80..3d26f4d 100644 --- a/app/components/Header.tsx +++ b/app/components/Header.tsx @@ -1,12 +1,13 @@ import React, { useState, useRef, useEffect } from 'react'; -import Login from './Login'; +import Login from './Login'; // Importing the Login component +// Define the props for the Header component interface HeaderProps { - onViewChange: (view: 'AI' | 'FAQ' | 'Documentation' | 'Credits') => void; - showDivs: boolean; - toggleDivs: () => void; - showHistoryModelsToggle: boolean; - showToggle: boolean; + onViewChange: (view: 'AI' | 'FAQ' | 'Documentation' | 'Credits') => void; // Function to change views + showDivs: boolean; // State to show/hide divs + toggleDivs: () => void; // Function to toggle divs + showHistoryModelsToggle: boolean; // State to show/hide history models + showToggle: boolean; // State to show/hide toggle button } const Header: React.FC = ({ @@ -16,50 +17,53 @@ const Header: React.FC = ({ showHistoryModelsToggle, showToggle, }) => { + // State to manage menu open/closed state const [menuOpen, setMenuOpen] = useState(false); - const dropdownRef = useRef(null); - const toggleRef = useRef(null); + const dropdownRef = useRef(null); // Ref for dropdown menu + const toggleRef = useRef(null); // Ref for hamburger toggle - // Pages that will be displayed in the menu + // Pages to be displayed in the navigation menu const pages: ('AI' | 'FAQ' | 'Documentation' | 'Credits')[] = ['AI', 'FAQ', 'Documentation', 'Credits']; - // Toggle menu state + // Function to toggle the dropdown menu state const toggleMenu = () => { setMenuOpen((prevMenuOpen) => !prevMenuOpen); }; - // Handle button click + // Function to handle view change when a menu item is clicked const handleViewChange = (page: 'AI' | 'FAQ' | 'Documentation' | 'Credits') => { - onViewChange(page); - setMenuOpen(false); // Close the menu when a button is clicked + onViewChange(page); // Call the onViewChange function with the selected page + setMenuOpen(false); // Close the menu after selection }; - // Effect to handle clicks outside of the dropdown + // Effect to handle clicks outside the dropdown to close it useEffect(() => { const handleClickOutside = (event: MouseEvent) => { + // Check if the click is outside the dropdown and toggle elements if ( dropdownRef.current && !dropdownRef.current.contains(event.target as Node) && toggleRef.current && !toggleRef.current.contains(event.target as Node) ) { - setMenuOpen(false); + setMenuOpen(false); // Close the menu if the click is outside } }; - document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('mousedown', handleClickOutside); // Listen for clicks return () => { - document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('mousedown', handleClickOutside); // Cleanup listener on unmount }; }, []); return ( <>
+ {/* Show the toggle button for divs if conditions are met */} {showToggle && showHistoryModelsToggle && ( )} + + {/* Navigation menu */} + + {/* Hamburger menu toggle */}
- - - + {/* Top bar of the hamburger */} + {/* Middle bar of the hamburger */} + {/* Bottom bar of the hamburger */}
+
- {/* AI logo or text */} + {/* Placeholder for AI logo or text */}
- + {/* Include the Login component */}
); }; -export default Header; +export default Header; // Exporting the Header component diff --git a/app/components/History.tsx b/app/components/History.tsx index ad8e9af..a479b70 100644 --- a/app/components/History.tsx +++ b/app/components/History.tsx @@ -1,96 +1,114 @@ import React, { useState } from 'react'; -import { useChatHistory } from '../hooks/useChatHistory'; +import { useChatHistory } from '../hooks/useChatHistory'; // Importing the custom hook for chat history const History: React.FC = () => { - const [chatHistory, setSelectedIndex, setChatHistory] = useChatHistory() - const [isEditing, setIsEditing] = useState(false); - const [inputValue, setInputValue] = useState(''); - const [hoveredIndex, setHoveredIndex] = useState(null) - + // Destructuring values from the useChatHistory hook + const [chatHistory, setSelectedIndex, setChatHistory] = useChatHistory(); + const [isEditing, setIsEditing] = useState(false); // State to manage edit mode + const [inputValue, setInputValue] = useState(''); // State for input field + const [hoveredIndex, setHoveredIndex] = useState(null); // State to track hovered chat index + // Function to activate editing mode const handleEditButtonClick = () => { setIsEditing(true); }; + // Function to update input value as the user types const handleInputChange = (e: React.ChangeEvent) => { setInputValue(e.target.value); }; + // Function to save the new chat const handleSaveButtonClick = () => { - setIsEditing(false); - chatHistory.chats.push({ name: inputValue, messages: [], timestamp: 5 }) - setInputValue("") + setIsEditing(false); // Exit edit mode + // Add a new chat entry to the history + chatHistory.chats.push({ name: inputValue, messages: [], timestamp: 5 }); + setInputValue(''); // Reset input value }; + // Function to select a chat from the history const handleHistoryClick = (index: number) => { - setSelectedIndex(index) - console.log("index",index); - } + setSelectedIndex(index); // Set the selected index to the clicked chat + console.log("index", index); + }; - const handleHistoryHover = (index:number) => { - setHoveredIndex(index) - } + // Function to handle hover over a chat entry + const handleHistoryHover = (index: number) => { + setHoveredIndex(index); // Set hovered index + }; + // Function to reset hovered index when not hovering const handleHistoryNotHover = () => { - setHoveredIndex(null) - } - - console.log("chat length",chatHistory.chats.length) - console.log("index",chatHistory.selectedIndex) + setHoveredIndex(null); // Reset hovered index + }; + // Debugging information for chat history length and selected index + console.log("chat length", chatHistory.chats.length); + console.log("index", chatHistory.selectedIndex); + + // Function to delete a chat entry const handleHistoryDeletion = (index: number) => { - const currentIndex = chatHistory.selectedIndex; + const currentIndex = chatHistory.selectedIndex; // Get the currently selected index - // Create a new copy of the current chat history + // Create a new copy of the chat history const copyChats = { ...chatHistory }; - copyChats.chats = [...chatHistory.chats] - - // Remove the chat at the specified index - copyChats.chats.splice(index,1) + copyChats.chats = [...chatHistory.chats]; - // Determine new selectedIndex - let newSelectedIndex = currentIndex; + // Remove the chat at the specified index + copyChats.chats.splice(index, 1); - // Adjust selectedIndex based on the deletion - if (index === currentIndex) { - // If the deleted index is the currently selected one, reset the selected index - newSelectedIndex = copyChats.chats.length > 0 ? (index > 0 ? index - 1 : 0) : -1; // Set to previous or first chat or -1 if no chats left - } else if (index < currentIndex) { - // If the deleted chat is before the current selected index, decrement the selected index - newSelectedIndex = currentIndex - 1; + // Determine the new selectedIndex + let newSelectedIndex = currentIndex; + + // Adjust selectedIndex based on the deletion + if (index === currentIndex) { + // If the deleted index is currently selected, reset the selected index + newSelectedIndex = copyChats.chats.length > 0 ? (index > 0 ? index - 1 : 0) : -1; // Set to previous or first chat or -1 if no chats left + } else if (index < currentIndex) { + // If the deleted chat is before the current selected index, decrement the selected index + newSelectedIndex = currentIndex - 1; } - - copyChats.selectedIndex = newSelectedIndex - - console.log(copyChats) - // Set the updated chat history - setChatHistory(copyChats); -}; - + copyChats.selectedIndex = newSelectedIndex; // Update the selected index + + // Debugging information to inspect the modified chat history + console.log(copyChats); + + // Set the updated chat history + setChatHistory(copyChats); + }; + return (
) : ( )} @@ -120,4 +138,4 @@ const History: React.FC = () => { ); } - export default History; +export default History; // Exporting the History component diff --git a/app/components/Models.tsx b/app/components/Models.tsx index 469d9cb..3ac195c 100644 --- a/app/components/Models.tsx +++ b/app/components/Models.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; // Define all models that should be available. +//#region modelist const modelList = { 'Offline Fast': { model_type: 'local', @@ -112,7 +113,7 @@ const selectedAIFunction = [ 'Language', 'Weather' ] - +//#region variables const ModelSection: React.FC = () => { // Initialize state with value from localStorage or default to '' const [selectedModelDropdown, setSelectedModelDropdown] = useState(''); @@ -120,7 +121,7 @@ const ModelSection: React.FC = () => { const [activeSelectedAIFunction, setActiveSelectedAIFunction] = useState(''); const [currentSelectedAIFunction, setCurrentSelectedAIFunction] = useState(""); const [isOpenSourceMode, setIsOpenSourceMode] = useState("false") - +//#region functions useEffect(() => { if (typeof localStorage !== 'undefined') { const defaultValues = { @@ -175,7 +176,7 @@ const ModelSection: React.FC = () => { modelClicked(model) } }; - + //#region dropdown // Determine the filtered models based on current radioSelection const filteredModels = (() => { let models = []; @@ -241,7 +242,7 @@ const ModelSection: React.FC = () => { localStorage.setItem("type", modelList[selectedAIFunction]['model_type' as keyof typeof modelList[typeof selectedAIFunction]]) } } - + //#region return "html" return (
diff --git a/app/components/settings/ButtonSettings.tsx b/app/components/settings/ButtonSettings.tsx index c0372d3..20b0d47 100644 --- a/app/components/settings/ButtonSettings.tsx +++ b/app/components/settings/ButtonSettings.tsx @@ -1,17 +1,19 @@ import React from 'react'; +// Define the props for the ButtonSetting component interface ButtonSettingProps { label: string; // The label to display on the button onClick: () => void; // The function to call when the button is clicked className?: string; // Optional additional classes for styling } -const ButtonSetting: React.FC = ({ label, onClick }) => { +// Functional component definition +const ButtonSetting: React.FC = ({ label, onClick, className }) => { return ( -
+
{/* Container for the button */} diff --git a/app/components/settings/CheckBox.tsx b/app/components/settings/CheckBox.tsx index 1b1ece5..7944a7e 100644 --- a/app/components/settings/CheckBox.tsx +++ b/app/components/settings/CheckBox.tsx @@ -1,25 +1,28 @@ import React from 'react'; +// Define the props for the CheckboxSetting component interface CheckboxSettingProps { label: string; // The label to display checked: boolean; // The checked state of the checkbox setChecked: (value: boolean) => void; // Method to update the state } +// Functional component definition const CheckboxSetting: React.FC = ({ label, checked, setChecked }) => { + // Handler to toggle the checkbox state const handleCheckboxChange = () => { - setChecked(!checked); + setChecked(!checked); // Toggle the checked state }; return ( -
+
{/* Container for the checkbox setting */}
); diff --git a/app/components/settings/ColorSettings.tsx b/app/components/settings/ColorSettings.tsx index 4ada123..cc701dc 100644 --- a/app/components/settings/ColorSettings.tsx +++ b/app/components/settings/ColorSettings.tsx @@ -1,29 +1,32 @@ - import React from 'react'; +import React from 'react'; - interface ColorSettingProps { +// Define the props for the ColorSetting component +interface ColorSettingProps { name: string; // The name to display in the

tag value: string; // The current color value setValue: (newColor: string) => void; // The method to update the state cssVariable: string; // The CSS variable name to set - } +} - const ColorSetting: React.FC = ({ name, value, setValue, cssVariable }) => { +// Functional component definition +const ColorSetting: React.FC = ({ name, value, setValue, cssVariable }) => { + // Handler to change the color value const handleColorChange = (e: React.ChangeEvent) => { - const newColor = e.target.value; - setValue(newColor); - document.documentElement.style.setProperty(cssVariable, newColor); + const newColor = e.target.value; // Get the new color from the input + setValue(newColor); // Update the state with the new color + document.documentElement.style.setProperty(cssVariable, newColor); // Set the CSS variable }; return ( -

-

{name}

- +
{/* Container for the color setting */} +

{name}

{/* Display the name */} +
); - }; +}; - export default ColorSetting; +export default ColorSetting; diff --git a/app/components/settings/DropDown.tsx b/app/components/settings/DropDown.tsx index 9b0f79d..67f40ac 100644 --- a/app/components/settings/DropDown.tsx +++ b/app/components/settings/DropDown.tsx @@ -1,35 +1,39 @@ import React from 'react'; +// Define the structure of each option in the dropdown interface Option { - value: string; // The actual value to be used - label: string; // The label to display for the option + value: string; // The actual value to be used + label: string; // The label to display for the option } +// Define the props for the DropdownSetting component interface DropdownSettingProps { - label: string; // The label to display - value: string; // The current selected value - setValue: (newValue: string) => void; // The method to update the state - options: Option[]; // List of options for the dropdown + label: string; // The label to display above the dropdown + value: string; // The currently selected value + setValue: (newValue: string) => void; // Method to update the state with the new value + options: Option[]; // List of options for the dropdown } +// Functional component definition const DropdownSetting: React.FC = ({ label, value, setValue, options }) => { - const handleSelectChange = (e: React.ChangeEvent) => { - const newValue = e.target.value; - setValue(newValue); - }; + // Handler to change the selected option + const handleSelectChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; // Get the new selected value + setValue(newValue); // Update the state with the new value + }; - return ( -
- - -
- ); + return ( +
{/* Container for the dropdown setting */} + {/* Display the label */} + +
+ ); }; export default DropdownSetting; diff --git a/app/components/settings/DropDownTheme.tsx b/app/components/settings/DropDownTheme.tsx index 793a19d..e065989 100644 --- a/app/components/settings/DropDownTheme.tsx +++ b/app/components/settings/DropDownTheme.tsx @@ -2,9 +2,10 @@ import React from 'react'; const ThemeDropdown: React.FC<{ - selectedTheme: string; - setSelectedTheme: (theme: string) => void; + selectedTheme: string; // Currently selected theme + setSelectedTheme: (theme: string) => void; // Function to update the selected theme }> = ({ selectedTheme, setSelectedTheme }) => { + // Define available theme options const themeOptions = [ { value: 'IOMARKET', label: 'IOMARKET' }, { value: 'WHITE', label: 'WHITE' }, @@ -14,22 +15,22 @@ const ThemeDropdown: React.FC<{ ]; return ( -
-

Select Theme

+
{/* Container for the dropdown */} +

Select Theme

{/* Label for the dropdown */} diff --git a/app/components/settings/FontSize.tsx b/app/components/settings/FontSize.tsx index d126ca5..2f5ca1f 100644 --- a/app/components/settings/FontSize.tsx +++ b/app/components/settings/FontSize.tsx @@ -2,28 +2,29 @@ import React from 'react'; interface FontSizeSettingProps { - fontSize: string; // The current font size + fontSize: string; // The current font size as a string (e.g., "16px") setFontSize: (newSize: string) => void; // Function to update the font size } const FontSizeSetting: React.FC = ({ fontSize, setFontSize }) => { + // Handle changes to the font size input const handleFontSizeChange = (e: React.ChangeEvent) => { - const newSize = `${e.target.value}px`; - setFontSize(newSize); - document.documentElement.style.setProperty('--font-size', newSize); + const newSize = `${e.target.value}px`; // Create the new font size string + setFontSize(newSize); // Update the font size state + document.documentElement.style.setProperty('--font-size', newSize); // Update the CSS variable }; return ( -
-

Font Size

+
{/* Container for the font size setting */} +

Font Size

{/* Label for the setting */} - {fontSize} + {fontSize} {/* Display the current font size */}
); }; diff --git a/app/components/settings/OpenSourceToggle.tsx b/app/components/settings/OpenSourceToggle.tsx index 9e60415..3f9f4a1 100644 --- a/app/components/settings/OpenSourceToggle.tsx +++ b/app/components/settings/OpenSourceToggle.tsx @@ -12,25 +12,26 @@ const OpenSourceModeToggle: React.FC = ({ setOpenSourceMode, setSelectedOption }) => { + // Handle toggle change event const handleToggleChange = () => { - const newValue = !openSourceMode; - setOpenSourceMode(newValue); + const newValue = !openSourceMode; // Toggle the current state + setOpenSourceMode(newValue); // Update the open source mode state // Update radio selection based on the new openSourceMode value if (newValue) { setSelectedOption('FOSS'); // Set to FOSS if enabling open source mode } else { - setSelectedOption('None'); // Or any other default value when disabling + setSelectedOption('None'); // Set to a default value when disabling } }; return ( -
+
{/* Container for the toggle setting */} diff --git a/app/components/settings/PrivacySettings.tsx b/app/components/settings/PrivacySettings.tsx index e9cab20..3683430 100644 --- a/app/components/settings/PrivacySettings.tsx +++ b/app/components/settings/PrivacySettings.tsx @@ -14,28 +14,28 @@ const PrivacySettings: React.FC = ({ selectedOption, handl

Disable Options:

- {/* Offline */} + {/* Offline Option */}
handleRadioChange('Offline')} // Allow selection only if not in open-source mode + className={`slider-option ${selectedOption === 'Offline' ? 'active' : ''}`} // Active class based on selection + onClick={() => handleRadioChange('Offline')} // Handle selection change > - Offline tools{openSourceMode ? ' (FOSS)' : ''} + Offline tools{openSourceMode ? ' (FOSS)' : ''} {/* Display FOSS label if applicable */}
- {/* Online */} + {/* Online Option */}
handleRadioChange('Online')} + className={`slider-option ${selectedOption === 'Online' ? 'active' : ''}`} // Active class based on selection + onClick={() => handleRadioChange('Online')} // Handle selection change > - Online tools{openSourceMode ? ' (FOSS)' : ''} + Online tools{openSourceMode ? ' (FOSS)' : ''} {/* Display FOSS label if applicable */}
- {/* None */} + {/* None Option */}
handleRadioChange('None')} + className={`slider-option ${selectedOption === 'None' ? 'active' : ''}`} // Active class based on selection + onClick={() => handleRadioChange('None')} // Handle selection change > - None{openSourceMode ? ' (FOSS)' : ''} + None{openSourceMode ? ' (FOSS)' : ''} {/* Display FOSS label if applicable */}

diff --git a/app/components/settings/Settings.tsx b/app/components/settings/Settings.tsx index 83612d0..a5c72c0 100644 --- a/app/components/settings/Settings.tsx +++ b/app/components/settings/Settings.tsx @@ -65,7 +65,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( // Measurement setting const [preferredMeasurement, setPreferredMeasurement] = useState(() => localStorage.getItem('preferredMeasurement') || 'Metric'); - // Theme settings + //#region Theme settings const [backgroundColor, setBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--background-color').trim()); const [headerBackground, setHeaderBackground] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--header-background-color').trim()); const [textColor, setTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--text-color').trim()); @@ -122,6 +122,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( const [google, setGoogle] = useState(localStorage.getItem('google') || ""); const [myBoolean, setMyBoolean] = useState(() => getItemFromLocalStorage('myBoolean')); + //#region Json const settings = { userPreferences: { activeSection, @@ -185,6 +186,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( }, }; + //#region color settings const colorSettings = [ { name: "Background Color", value: backgroundColor, setValue: setBackgroundColor, cssVariable: "--background-color" }, { name: "Header Background Color", value: headerBackground, setValue: setHeaderBackground, cssVariable: "--header-background-color" }, @@ -219,7 +221,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( { name: "Burger Menu Background Color", value: burgerMenuBackgroundColor, setValue: setBurgerMenuBackgroundColor, cssVariable: "--burger-menu-background-color" }, ]; - + //#region time settings const timeZoneOptions = [ { value: 'GMT', label: 'GMT' }, { value: 'EST', label: 'EST' }, @@ -233,7 +235,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( { value: 'JST', label: 'JST' }, ]; - + //#region language settings const languageOptions = [ { code: 'en', name: 'English' }, { code: 'es', name: 'Spanish' }, @@ -246,7 +248,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( { code: 'ru', name: 'Russian' }, { code: 'ar', name: 'Arabic' }, ]; - + //#region currency settings const currencyOptions = [ { code: 'usd', name: 'USD' }, { code: 'eur', name: 'EUR' }, @@ -258,7 +260,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( { code: 'cny', name: 'CNY' }, { code: 'inr', name: 'INR' }, ]; - + //#region date settings const dateFormatOptions = [ { value: 'mm/dd/yyyy', label: 'MM/DD/YYYY' }, { value: 'dd/mm/yyyy', label: 'DD/MM/YYYY' }, @@ -276,7 +278,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( { value: 'Metric', label: 'Metric' }, { value: 'Imperial', label: 'Imperial' }, ]; - + //#region text settings const fontOptions = [ { value: "'Poppins', sans-serif", label: 'Poppins' }, { value: "'Inconsolata', monospace", label: 'Inconsolata' }, @@ -294,7 +296,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ( { value: "'Zilla Slab Highlight', serif", label: 'Zilla Slab Highlight' }, ]; - //#region Start of the code + //#region Function const handleLogout = () => { localStorage.clear(); alert('Successfully logged out!'); diff --git a/app/components/settings/settingUtils.ts b/app/components/settings/settingUtils.ts index f6cd00d..ebe0807 100644 --- a/app/components/settings/settingUtils.ts +++ b/app/components/settings/settingUtils.ts @@ -10,6 +10,7 @@ export function exportSettings(): string { for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key) { + // Exclude sensitive information if (key !== "accountName" && key !== "accountPassword" && key !== "accountEmail") { settings[key] = localStorage.getItem(key) || ""; } @@ -39,25 +40,28 @@ export function importSettings(jsonData: string): void { } } +// Send current settings to the database export const sendToDatabase = async () => { - const useName = localStorage.getItem("accountName") - const usePassword = localStorage.getItem("accountPassword") + const useName = localStorage.getItem("accountName"); + const usePassword = localStorage.getItem("accountPassword"); + if (useName && usePassword) { - const result = await changeSettings(useName, usePassword, JSON.parse(exportSettings())) - if (result == true) { + const result = await changeSettings(useName, usePassword, JSON.parse(exportSettings())); + if (result === true) { + // Only reload if the settings change was successful window.location.reload(); } } - window.location.reload(); }; +// Import settings from the database based on username and password export const importDatabase = async (useName: string, usePassword: string) => { const databaseSettings = await getSettings(useName, usePassword); // Ensure user settings exist before flattening and storing - if (typeof databaseSettings == 'object' && databaseSettings) { + if (typeof databaseSettings === 'object' && databaseSettings) { importSettings(JSON.stringify(databaseSettings, null, 2)); // Pass only the current user's settings } else { console.error('Database settings are not in the expected format.'); } -} +}; diff --git a/app/components/settings/theme.ts b/app/components/settings/theme.ts index 5c3a948..581b62a 100644 --- a/app/components/settings/theme.ts +++ b/app/components/settings/theme.ts @@ -1,3 +1,4 @@ +//#region IOMARKET export const applyIOMarketTheme = () => { document.documentElement.style.setProperty('--header-background-color', '#7e7e7e'); document.documentElement.style.setProperty('--header-text-color', '#ffffff'); @@ -35,7 +36,7 @@ export const applyIOMarketTheme = () => { document.documentElement.style.setProperty('--font-family', "'Poppins', 'sans-serif'"); document.documentElement.style.setProperty('--font-size', '16px'); }; - +//#region WHITE export const applyWhiteTheme = () => { document.documentElement.style.setProperty('--header-background-color', '#f0f0f0'); // Lighter header background document.documentElement.style.setProperty('--header-text-color', '#333'); // Dark text for contrast @@ -73,7 +74,7 @@ export const applyWhiteTheme = () => { document.documentElement.style.setProperty('--font-family', "'Poppins', 'sans-serif'"); // Same font family document.documentElement.style.setProperty('--font-size', '16px'); // Same font size }; - +//#region BLACK export const applyBlackTheme = () => { document.documentElement.style.setProperty('--header-background-color', '#1a1a1a'); // Dark header background document.documentElement.style.setProperty('--header-text-color', '#ffffff'); // White text for header @@ -112,7 +113,7 @@ export const applyBlackTheme = () => { document.documentElement.style.setProperty('--font-size', '16px'); // Font size }; - +//#region CUSTOM export const applyCustomTheme = () => { if (typeof localStorage !== 'undefined') { const themeVariables = { @@ -191,6 +192,8 @@ export const applyCustomTheme = () => { }; } +//#region BASIC-CUSTOM + // TypeScript types for color parameters type Color = string; @@ -277,6 +280,7 @@ const lightenColor = (color: Color, percent: number): Color => { return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; }; +//#region APPLY-THEME // This is the new function that calls the appropriate theme application export const applyTheme = (theme: string, primary: string, secondary: string, accent: string, background: string, text: string) => { switch (theme) { diff --git a/app/hooks/useChatHistory.tsx b/app/hooks/useChatHistory.tsx index b2aceb0..38bb6b9 100644 --- a/app/hooks/useChatHistory.tsx +++ b/app/hooks/useChatHistory.tsx @@ -1,68 +1,98 @@ -import { useEffect, useState } from "react" +import { useEffect, useState } from "react"; +// Define the structure of a Message interface Message { - role: string - content:string + role: string; // The role of the message sender (e.g., system, user, assistant) + content: string; // The content of the message } +// Define the structure for each chat session interface ChatMessages { - name: string - messages: Message[] - timestamp: number - + name: string; // The name or title of the chat + messages: Message[]; // Array of messages in the chat + timestamp: number; // Timestamp for the chat session } +// Define the structure for the global chat history interface GlobalChatHistory { - chats: ChatMessages[] - selectedIndex: number + chats: ChatMessages[]; // Array of chat sessions + selectedIndex: number; // Index of the currently selected chat } +// Initial global chat history state let globalChatHistory: GlobalChatHistory = { chats: [ - { name: "Welcome!", messages: [{role:"system",content:"you are a helpful assistant"},{role:"assistant",content:"Hello! How can I help you?"}], timestamp: 4 }, + { + name: "Welcome!", + messages: [ + { role: "system", content: "you are a helpful assistant" }, + { role: "assistant", content: "Hello! How can I help you?" } + ], + timestamp: 4 + }, ], - selectedIndex:0 -} -let listeners: ((state: GlobalChatHistory) => void)[] = [] + selectedIndex: 0 +}; +// Listeners for state changes +let listeners: ((state: GlobalChatHistory) => void)[] = []; + +// Function to set a new global state and notify listeners const setGlobalState = (newState: GlobalChatHistory): void => { - globalChatHistory = newState; - listeners.forEach((listener) => listener(globalChatHistory)) + globalChatHistory = newState; // Update the global state + listeners.forEach((listener) => listener(globalChatHistory)); // Notify all listeners } -export const useChatHistory = (): [GlobalChatHistory, (index:number)=>void, (newState:GlobalChatHistory) => void,(messageIndex: number, newContent:string)=> void] => { - const [state, setState] = useState(globalChatHistory) +// Custom hook to manage chat history +export const useChatHistory = (): [ + GlobalChatHistory, // Current state + (index: number) => void, // Function to set selected index + (newState: GlobalChatHistory) => void, // Function to set global state + (messageIndex: number, newContent: string) => void // Function to update a message +] => { + const [state, setState] = useState(globalChatHistory); // Local state initialized with global state useEffect(() => { - console.log("help", globalChatHistory); + // Log the initial state for debugging + console.log("Current global chat history:", globalChatHistory); + // Listener to update local state when global state changes const listener = (newState: GlobalChatHistory) => { - setState(newState) - } + setState(newState); + }; - listeners.push(listener) + listeners.push(listener); // Add the listener to the array + // Cleanup function to remove the listener when the component unmounts return () => { - listeners = listeners.filter((l) => l!== listener) - } - }, []) + listeners = listeners.filter((l) => l !== listener); + }; + }, []); + // Function to set the selected chat index const setSelectedIndex = (index: number) => { - setGlobalState({...state,selectedIndex:index}) + setGlobalState({ ...state, selectedIndex: index }); // Update the global state } + // Function to update a specific message in the current chat const updateMessage = (messageIndex: number, newContent: string) => { - const updatedChats = [...state.chats] - const chatIndex = globalChatHistory.selectedIndex + const updatedChats = [...state.chats]; // Make a copy of the current chats + const chatIndex = globalChatHistory.selectedIndex; // Get the currently selected chat index + + // Check if the selected chat index is valid if (chatIndex >= 0 && chatIndex < updatedChats.length) { - const updatedMessages = [...updatedChats[chatIndex].messages] + const updatedMessages = [...updatedChats[chatIndex].messages]; // Copy messages of the selected chat + + // Check if the message index is valid if (messageIndex >= 0 && messageIndex < updatedMessages.length) { - updatedMessages[messageIndex] = { ...updatedMessages[messageIndex], content: newContent } - updatedChats[chatIndex] = { ...updatedChats[chatIndex], messages: updatedMessages } - setGlobalState({...state, chats: updatedChats}) + // Update the content of the specified message + updatedMessages[messageIndex] = { ...updatedMessages[messageIndex], content: newContent }; + updatedChats[chatIndex] = { ...updatedChats[chatIndex], messages: updatedMessages }; // Update the chat with new messages + setGlobalState({ ...state, chats: updatedChats }); // Set the updated global state } } } - return [state, setSelectedIndex, setGlobalState, updateMessage] -} \ No newline at end of file + // Return the current state and action functions + return [state, setSelectedIndex, setGlobalState, updateMessage]; +} diff --git a/app/layout.tsx b/app/layout.tsx index 1d7d206..e00a7ef 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,24 +1,23 @@ import { ReactNode } from 'react'; +// Metadata for the application, used for SEO and page information export const metadata = { - title: 'AI Assistant | Interstellar Development', - description: 'A little AI chat that is able to assist you in little tasks', + title: 'AI Assistant | Interstellar Development', // Title of the page + description: 'A little AI chat that is able to assist you in little tasks', // Description of the page }; - - - +// RootLayout component definition export default function RootLayout({ children }: { children: ReactNode }) { return ( - + {/* Set the language of the document */} - {metadata.title} - - {/* Tried adding the favicon here */} + {metadata.title} {/* Set the page title */} + {/* Set the page description for SEO */} + {/* Link to the favicon for the site */} -
{children}
+
{children}
{/* Render the child components inside the main element */} ); diff --git a/app/page.tsx b/app/page.tsx index 3c77911..cb1b2ec 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,21 +7,30 @@ import Documentation from './components/Documentation'; // Ensure the import pat import History from './components/History'; import Models from './components/Models'; import Credits from './components/Credits'; -import { applyIOMarketTheme, applyWhiteTheme, applyBlackTheme, applyCustomTheme, applyBasicCustomTheme } from './components/settings/theme'; +import { + applyIOMarketTheme, + applyWhiteTheme, + applyBlackTheme, + applyCustomTheme, + applyBasicCustomTheme +} from './components/settings/theme'; import './styles/master.css'; const LandingPage: React.FC = () => { + // State to control visibility of the left panels const [showDivs, setShowDivs] = useState(true); + // State to track which view is currently displayed const [view, setView] = useState<'AI' | 'FAQ' | 'Documentation' | 'Credits'>('AI'); const conversationRef = useRef(null); - const [primaryColor, setPrimaryColor] = useState("#fefefe"); - const [secondaryColor, setSecondaryColor] = useState("#fefefe"); - const [accentColor, setAccentColor] = useState("#fefefe"); - const [basicBackgroundColor, setBasicBackgroundColor] = useState("#fefefe"); - const [basicTextColor, setBasicTextColor] = useState("#fefefe"); + // State for theme colors + const [primaryColor, setPrimaryColor] = useState("#fefefe"); + const [secondaryColor, setSecondaryColor] = useState("#fefefe"); + const [accentColor, setAccentColor] = useState("#fefefe"); + const [basicBackgroundColor, setBasicBackgroundColor] = useState("#fefefe"); + const [basicTextColor, setBasicTextColor] = useState("#fefefe"); - // Synchronize state with local storage on mount + // Synchronize theme colors with local storage on component mount useEffect(() => { if (typeof localStorage !== 'undefined') { setPrimaryColor(localStorage.getItem("primaryColor") || "#fefefe"); @@ -32,18 +41,21 @@ const LandingPage: React.FC = () => { } }, [primaryColor, secondaryColor, accentColor, basicBackgroundColor, basicTextColor]); + // Toggle visibility of the left panels const toggleDivs = () => { setShowDivs(prevState => !prevState); }; + // Change the current view based on user selection const handleViewChange = (view: 'AI' | 'FAQ' | 'Documentation' | 'Credits') => { setView(view); + // Hide left panels if the selected view is not 'AI' if (view !== 'AI') { setShowDivs(false); } }; - // Apply theme based on selectedTheme and color settings + // Apply the selected theme and color settings based on local storage useEffect(() => { if (typeof localStorage !== 'undefined') { const savedTheme = localStorage.getItem('selectedTheme'); @@ -52,8 +64,8 @@ const LandingPage: React.FC = () => { case 'IOMARKET': applyIOMarketTheme(); break; - case 'WHITE': - applyWhiteTheme(); + case 'WHITE': + applyWhiteTheme(); break; case 'BLACK': applyBlackTheme(); @@ -61,8 +73,8 @@ const LandingPage: React.FC = () => { case 'CUSTOM': applyCustomTheme(); break; - case 'BASIC-CUSTOM': - applyBasicCustomTheme( + case 'BASIC-CUSTOM': + applyBasicCustomTheme( primaryColor, secondaryColor, accentColor, @@ -71,15 +83,16 @@ const LandingPage: React.FC = () => { ); break; default: - applyIOMarketTheme(); + applyIOMarketTheme(); // Fallback theme break; } } } - }, [primaryColor, secondaryColor, accentColor, basicBackgroundColor, basicTextColor]); // Watch color states and apply themes accordingly + }, [primaryColor, secondaryColor, accentColor, basicBackgroundColor, basicTextColor]); // Apply themes whenever color states change return ( <> + {/* Header component with props for toggling and view change */}
{
{showDivs && (
+ {/* Show History and Models components if left panels are visible */}
)}
+ {/* Render the selected view based on the current state */} {view === 'AI' && } {view === 'FAQ' && } {view === 'Documentation' && } diff --git a/py/ai.py b/py/ai.py index 0207180..d4cd09d 100644 --- a/py/ai.py +++ b/py/ai.py @@ -4,10 +4,11 @@ import google.generativeai as genai import anthropic import ollama - class AI: @staticmethod def process_local(model, messages, return_class, access_token): + """Process chat messages using the Ollama model locally.""" + # Stream the chat response from the Ollama model stream = ollama.chat( model=model, messages=messages, @@ -15,50 +16,61 @@ class AI: options={"temperature": 0.5}, ) + # Initialize the AI response for the given access token with return_class.ai_response_lock: return_class.ai_response[access_token] = "" + # Collect the response chunks and append to the response for the given access token for chunk in stream: with return_class.ai_response_lock: return_class.ai_response[access_token] += chunk["message"]["content"] @staticmethod def process_mistralai(model, messages, return_class, access_token, api_key): + """Process chat messages using the Mistral AI model.""" client = Mistral(api_key=api_key) + # Stream the chat response from the Mistral model stream_response = client.chat.stream(model=model, messages=messages) + # Initialize the AI response for the given access token with return_class.ai_response_lock: return_class.ai_response[access_token] = "" + # Collect the response chunks and append to the response for the given access token for chunk in stream_response: with return_class.ai_response_lock: - return_class.ai_response[access_token] += chunk.data.choices[ - 0 - ].delta.content + return_class.ai_response[access_token] += chunk.data.choices[0].delta.content @staticmethod def process_openai(model, messages, return_class, access_token, api_key): + """Process chat messages using the OpenAI model.""" client = OpenAI(api_key=api_key) + # Stream the chat response from the OpenAI model stream_response = client.chat.completions.create( model=model, messages=messages, stream=True ) + # Initialize the AI response for the given access token with return_class.ai_response_lock: return_class.ai_response[access_token] = "" + # Collect the response chunks and append to the response for the given access token for chunk in stream_response: with return_class.ai_response_lock: return_class.ai_response[access_token] += chunk.choices[0].delta.content @staticmethod def process_anthropic(model, messages, return_class, access_token, api_key): + """Process chat messages using the Anthropic model.""" client = anthropic.Anthropic(api_key=api_key) + # Initialize the AI response for the given access token with return_class.ai_response_lock: return_class.ai_response[access_token] = "" + # Stream the chat response from the Anthropic model with client.messages.stream( max_tokens=1024, model=model, @@ -70,24 +82,27 @@ class AI: @staticmethod def process_google(model, messages, return_class, access_token, api_key): - message = messages[-1]["content"] - messages.pop() + """Process chat messages using the Google Generative AI model.""" + message = messages[-1]["content"] # Get the latest message content + messages.pop() # Remove the latest message from the list + # Prepare messages for the Google Generative AI format for msg in messages: msg["parts"] = msg.pop()["content"] + # Change 'assistant' role to 'model' for compatibility for msg in messages: if msg["role"] == "assistant": msg["role"] = "model" + # Configure the Google Generative AI client genai.configure(api_key=api_key) + # Start a chat session with the specified model and message history model = genai.GenerativeModel(model) + chat = model.start_chat(history=messages) - chat = model.start_chat( - history=messages, - ) - + # Send the message and stream the response response = chat.send_message(message, stream=True) for chunk in response: return_class.ai_response[access_token] += chunk.text diff --git a/py/api.py b/py/api.py index 70c848e..6ba23c8 100644 --- a/py/api.py +++ b/py/api.py @@ -1,5 +1,4 @@ from time import sleep - from flask import Flask, request, jsonify from flask_cors import CORS import secrets @@ -13,53 +12,58 @@ from tts import TTS class API: def __init__(self): - self.crypt_size = 64 - self.app = Flask(__name__) - self.ai_response = {} - self.ai = AI() - self.db = DB() - self.weather = Weather() - self.voice = VoiceRecognition() - self.tts = TTS() - self.db.load_database() - self.ai_response_lock = threading.Lock() - CORS(self.app) + # Initialize the API class with necessary components and configurations + self.crypt_size = 64 # Size for generating secure tokens + self.app = Flask(__name__) # Create a Flask app instance + self.ai_response = {} # Dictionary to store AI responses keyed by access token + self.ai = AI() # AI processing instance + self.db = DB() # Database instance + self.weather = Weather() # Weather data retrieval instance + self.voice = VoiceRecognition() # Voice recognition instance + self.tts = TTS() # Text-to-Speech instance + self.db.load_database() # Load the database on startup + self.ai_response_lock = threading.Lock() # Lock for thread-safe access to AI responses + CORS(self.app) # Enable Cross-Origin Resource Sharing def run(self): + # Route to create a new AI session @self.app.route("/interstellar_ai/api/ai_create", methods=["GET"]) def create_ai(): - access_token = secrets.token_urlsafe(self.crypt_size) + access_token = secrets.token_urlsafe(self.crypt_size) # Generate a secure access token + # Ensure the access token is unique while access_token in self.ai_response: access_token = secrets.token_urlsafe(self.crypt_size) - self.ai_response[access_token] = "" + self.ai_response[access_token] = "" # Initialize the response for the new session return jsonify({"status": 200, "access_token": access_token}) + # Route to send messages to the AI @self.app.route("/interstellar_ai/api/ai_send", methods=["POST"]) def send_ai(): - data = request.get_json() - messages = data.get("messages") - model_type = data.get("model_type") - ai_model = data.get("ai_model") - access_token = data.get("access_token") + data = request.get_json() # Get JSON data from the request + messages = data.get("messages") # Extract messages + model_type = data.get("model_type") # Extract model type + ai_model = data.get("ai_model") # Extract AI model name + access_token = data.get("access_token") # Extract access token - print(model_type) + print(model_type) # Debugging output if access_token not in self.ai_response: - return jsonify({"status": 401, "error": "Invalid access token"}) + return jsonify({"status": 401, "error": "Invalid access token"}) # Token validation + # Start a new thread to process AI requests based on the model type if model_type == "local": thread = threading.Thread( target=self.ai.process_local, args=(ai_model, messages, self, access_token), ) thread.start() - thread.join() - sleep(0.5) + thread.join() # Wait for the thread to complete + sleep(0.5) # Sleep for a short duration return jsonify({"status": 200}) elif model_type == "mistral": - print(model_type) - api_key = data.get("api_key") + print(model_type) # Debugging output + api_key = data.get("api_key") # Get API key thread = threading.Thread( target=self.ai.process_mistralai, args=(ai_model, messages, self, access_token, api_key), @@ -69,7 +73,7 @@ class API: sleep(0.5) return jsonify({"status": 200}) elif model_type == "openai": - api_key = data.get("api_key") + api_key = data.get("api_key") # Get API key thread = threading.Thread( target=self.ai.process_openai, args=(ai_model, messages, self, access_token, api_key), @@ -79,7 +83,7 @@ class API: sleep(0.5) return jsonify({"status": 200}) elif model_type == "anthropic": - api_key = data.get("api_key") + api_key = data.get("api_key") # Get API key thread = threading.Thread( target=self.ai.process_anthropic, args=(ai_model, messages, self, access_token, api_key), @@ -89,7 +93,7 @@ class API: sleep(0.5) return jsonify({"status": 200}) elif model_type == "google": - api_key = data.get("api_key") + api_key = data.get("api_key") # Get API key thread = threading.Thread( target=self.ai.process_google, args=(ai_model, messages, self, access_token, api_key), @@ -99,79 +103,85 @@ class API: sleep(0.5) return jsonify({"status": 200}) - return jsonify({"status": 401, "error": "Invalid AI model type"}) + return jsonify({"status": 401, "error": "Invalid AI model type"}) # Model type validation + # Route to retrieve AI response based on access token @self.app.route("/interstellar_ai/api/ai_get", methods=["GET"]) def get_ai(): - data = request.args.get("access_token") + data = request.args.get("access_token") # Get access token from query parameters if data not in self.ai_response: - return jsonify({"status": 401, "error": "Invalid access token"}) - return jsonify({"status": 200, "response": self.ai_response[data]}) + return jsonify({"status": 401, "error": "Invalid access token"}) # Token validation + return jsonify({"status": 200, "response": self.ai_response[data]}) # Return AI response + # Route for database operations @self.app.route("/interstellar_ai/db", methods=["POST"]) def db_manipulate(): - sent_data = request.get_json() - action = sent_data.get("action") + sent_data = request.get_json() # Get JSON data from the request + action = sent_data.get("action") # Extract action type if action == "create_account": - return jsonify({"status": 200, "response": self.db.add_user(sent_data)}) + return jsonify({"status": 200, "response": self.db.add_user(sent_data)}) # Add user elif action == "change_password": return jsonify( - {"status": 200, "response": self.db.update_password(sent_data)} + {"status": 200, "response": self.db.update_password(sent_data)} # Update password ) elif action == "get_settings": return jsonify( - {"status": 200, "response": self.db.get_settings(sent_data)} + {"status": 200, "response": self.db.get_settings(sent_data)} # Get user settings ) elif action == "change_settings": return jsonify( - {"status": 200, "response": self.db.change_settings(sent_data)} + {"status": 200, "response": self.db.change_settings(sent_data)} # Change user settings ) elif action == "get_history": return jsonify( - {"status": 200, "response": self.db.get_history(sent_data)} + {"status": 200, "response": self.db.get_history(sent_data)} # Get user history ) elif action == "change_history": return jsonify( - {"status": 200, "response": self.db.change_history(sent_data)} + {"status": 200, "response": self.db.change_history(sent_data)} # Change user history ) elif action == "check_credentials": return jsonify( - {"status": 200, "response": self.db.check_credentials(sent_data)} + {"status": 200, "response": self.db.check_credentials(sent_data)} # Check user credentials ) elif action == "delete_account": return jsonify( - {"status": 200, "response": self.db.delete_user(sent_data)} + {"status": 200, "response": self.db.delete_user(sent_data)} # Delete user account ) elif action == "get_email": return jsonify( - {"status": 200, "response": self.db.get_email(sent_data)} + {"status": 200, "response": self.db.get_email(sent_data)} # Get user email ) elif action == "get_name": - return jsonify({"status": 200, "response": self.db.get_name(sent_data)}) + return jsonify({"status": 200, "response": self.db.get_name(sent_data)}) # Get user name - return jsonify({"status": 401, "response": "Invalid action"}) + return jsonify({"status": 401, "response": "Invalid action"}) # Action validation + # Route for voice recognition @self.app.route("/interstellar_ai/api/voice_recognition", methods=["POST"]) def voice_recognition(): - audio = request.files.get("audio") - text = self.voice.recognition(audio) - return jsonify({"status": 200, "response": text}) + audio = request.files.get("audio") # Get audio file from request + text = self.voice.recognition(audio) # Perform voice recognition + return jsonify({"status": 200, "response": text}) # Return recognized text + # Route for weather information retrieval @self.app.route("/interstellar_ai/api/weather", methods=["POST"]) def get_weather(): - sent_data = request.get_json() - unit_type = sent_data.get("unit_type") - city = sent_data.get("city") - weather_data = self.weather.getweather(unit_type, city) - return jsonify({"status": 200, "response": weather_data}) + sent_data = request.get_json() # Get JSON data from the request + unit_type = sent_data.get("unit_type") # Extract unit type (metric, imperial) + city = sent_data.get("city") # Extract city name + weather_data = self.weather.getweather(unit_type, city) # Get weather data + return jsonify({"status": 200, "response": weather_data}) # Return weather data - self.app.run(debug=True, host="0.0.0.0", port=5000) + self.app.run(debug=True, host="0.0.0.0", port=5000) # Start the Flask app + # Route for Text-to-Speech conversion @self.app.route("/interstellar_ai/api/tts", methods=["POST"]) def tts(): - text = request.args.get("text") - return jsonify({"status": 200, "response": self.tts.gen_tts(text)}) + text = request.args.get("text") # Get text from query parameters + return jsonify({"status": 200, "response": self.tts.gen_tts(text)}) # Generate TTS and return response +# Initialize the API class and run the application api = API() api.run() diff --git a/py/db.py b/py/db.py index c968561..fa8ab77 100644 --- a/py/db.py +++ b/py/db.py @@ -3,12 +3,13 @@ import json import os import pycouchdb - class DB: def __init__(self): + # Initialize the database dictionary to store user data self.database = {} def ensure_username(self, data): + # Ensure a username can be retrieved either from username or email if "username" in data: return data.get("username") elif "email" in data: @@ -18,15 +19,17 @@ class DB: @staticmethod def hash_password(password): - salt = "your_secret_salt" + # Hash the password with a salt for secure storage + salt = "your_secret_salt" # Consider using a secure random salt hashed_password = hashlib.sha256((password + salt).encode()).hexdigest() return hashed_password def add_user(self, data): + # Add a new user to the database if username is unique username = data.get("username") password = data.get("password") email = data.get("email") - hashed_password = self.hash_password(password) + hashed_password = self.hash_password(password) # Hash the password user_data = { "hashed_password": hashed_password, "email": email, @@ -35,112 +38,123 @@ class DB: } if username not in self.database: self.database[username] = user_data - self.save_database() + self.save_database() # Save changes to the database return True - return False + return False # User already exists def delete_user(self, data): + # Delete a user from the database if credentials are valid username = self.ensure_username(data) if not self.check_credentials(data): - return False + return False # Invalid credentials - del self.database[username] - self.save_database() + del self.database[username] # Remove user from database + self.save_database() # Save changes return True def update_password(self, data): + # Update the user's password if credentials are valid username = self.ensure_username(data) new_password = data.get("new_password") if not self.check_credentials(data): - return False + return False # Invalid credentials - hashed_new_password = self.hash_password(new_password) + hashed_new_password = self.hash_password(new_password) # Hash the new password self.database[username].update({"hashed_password": hashed_new_password}) - self.save_database() + self.save_database() # Save changes return True def check_credentials(self, data): + # Verify if provided credentials match stored data username = self.ensure_username(data) password = data.get("password") if username not in self.database: - return False + return False # User not found stored_hashed_password = self.database[username]["hashed_password"] entered_hashed_password = self.hash_password(password) - return stored_hashed_password == entered_hashed_password + return stored_hashed_password == entered_hashed_password # Check hashed password def change_settings(self, data): + # Change user settings if credentials are valid username = self.ensure_username(data) if not self.check_credentials(data): - return False + return False # Invalid credentials - self.database[username]["settings"] = data.get("data") - self.save_database() + self.database[username]["settings"] = data.get("data") # Update settings + self.save_database() # Save changes return True def get_settings(self, data): + # Retrieve user settings if credentials are valid username = self.ensure_username(data) if not self.check_credentials(data): - return None + return None # Invalid credentials - send_back = self.database[username].get("settings") + send_back = self.database[username].get("settings") # Get settings return send_back def change_history(self, data): + # Change user history if credentials are valid username = self.ensure_username(data) if not self.check_credentials(data): - return False + return False # Invalid credentials - self.database[username]["history"] = data.get("data") - self.save_database() + self.database[username]["history"] = data.get("data") # Update history + self.save_database() # Save changes return True def get_history(self, data): + # Retrieve user history if credentials are valid username = self.ensure_username(data) if not self.check_credentials(data): - return None + return None # Invalid credentials - send_back = self.database[username].get("history") + send_back = self.database[username].get("history") # Get history return send_back def get_email(self, data): + # Retrieve user email if credentials are valid username = self.ensure_username(data) if not self.check_credentials(data): - return None + return None # Invalid credentials - send_back = self.database[username].get("email") + send_back = self.database[username].get("email") # Get email return send_back def get_name(self, data): + # Retrieve username if credentials are valid if not self.check_credentials(data): - return None + return None # Invalid credentials - send_back = self.ensure_username(data) + send_back = self.ensure_username(data) # Get username return send_back def save_database(self): + # Save the database to the specified storage (CouchDB or JSON file) if os.environ.get("PRODUCTION") == "YES": server = pycouchdb.Server("http://admin:admin@localhost:5984/") db = server.database("interstellar_ai") - db.save(self.database) + db.save(self.database) # Save to CouchDB else: with open("database.json", "w") as file: - json.dump(self.database, file) + json.dump(self.database, file) # Save to JSON file def load_database(self): + # Load the database from the specified storage (CouchDB or JSON file) if os.environ.get("PRODUCTION") == "YES": server = pycouchdb.Server("http://admin:admin@localhost:5984/") db = server.database("interstellar_ai") if db: - self.database = db + self.database = db # Load from CouchDB else: - server.create("interstellar_ai") + server.create("interstellar_ai") # Create database if it doesn't exist db = server.database("interstellar_ai") - db.save(self.database) + db.save(self.database) # Save initial empty database else: try: with open("database.json", "r") as file: - self.database = json.load(file) + self.database = json.load(file) # Load from JSON file except FileNotFoundError: - pass + pass # File not found, do nothing diff --git a/py/tts.py b/py/tts.py index 93f7fa4..3fd30f5 100644 --- a/py/tts.py +++ b/py/tts.py @@ -1,6 +1,5 @@ import pyttsx3 - class TTS: @staticmethod def gen_tts(text): diff --git a/py/voice.py b/py/voice.py index 8aeb3e0..536407f 100644 --- a/py/voice.py +++ b/py/voice.py @@ -6,20 +6,28 @@ from pydub import AudioSegment class VoiceRecognition: @staticmethod def recognition(audio): + # Read the audio file into a BytesIO buffer audio_buffer = io.BytesIO(audio.read()) + # Load the audio file using pydub audio_segment = AudioSegment.from_file(audio_buffer, format="ogg") + # Export the audio to a WAV format in a BytesIO buffer wav_io = io.BytesIO() audio_segment.export(wav_io, format="wav") - wav_io.seek(0) + wav_io.seek(0) # Reset the buffer pointer to the start - model_size = "base" + # Load the Whisper model + model_size = "base" # Specify the model size model = WhisperModel(model_size, device="cpu", compute_type="int8") + # Transcribe the audio segments, _ = model.transcribe(wav_io) transcription = "" + + # Combine the transcribed segments into a single string for segment in segments: transcription += segment.text + " " - result = transcription.strip() + + result = transcription.strip() # Strip any leading/trailing whitespace return result diff --git a/py/weather.py b/py/weather.py index 41444c3..c64df0b 100644 --- a/py/weather.py +++ b/py/weather.py @@ -15,6 +15,7 @@ class Weather: async with python_weather.Client(unit=unit_type) as client: weather = await client.get(city) + # Collect weather data data = { "temperature": weather.temperature, "humidity": weather.humidity,