diff --git a/.gitignore b/.gitignore index fb2020f..eb7d642 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ cert.pem api_key.txt database.json + 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 3bf20f0..41fad9d 100644 --- a/app/backend/InputOutputHandler.tsx +++ b/app/backend/InputOutputHandler.tsx @@ -4,19 +4,19 @@ import ConversationFrontend from '../components/ConversationFrontend'; import InputFrontend from "../components/InputFrontend"; import { sendToVoiceRecognition } from "./voice_backend" import axios from "axios"; -import { useChatHistory } from '../hooks/useChatHistory'; +import { updateMessage, useChatHistory } from '../hooks/useChatHistory'; import { getWeather } from "./weather"; import { changeHistory, getHistory } from "./database"; const InputOutputBackend: React.FC = () => { - // # variables + //#region variables type Message = { role: string content: string } // Define state variables for user preferences and messages - const [chatHistory, setSelectedIndex, setChatHistory, updateMessage] = useChatHistory() + const [chatHistory, setChatHistory] = useChatHistory() const [preferredCurrency, setPreferredCurrency] = useState("USD"); const [preferredLanguage, setPreferredLanguage] = useState("english"); const [timeFormat, setTimeFormat] = useState("24-hour"); @@ -36,18 +36,13 @@ const InputOutputBackend: React.FC = () => { apiURL.hostname = "localhost" } - console.log(setSelectedIndex) - + //#region useEffect useEffect(() => { setMessages(chatHistory.chats[chatHistory.selectedIndex].messages) }, [chatHistory.selectedIndex]) useEffect(() => { - console.log("History", chatHistory); - console.log("Messages", messages); - - // Get the current chat's messages const currentMessages = chatHistory.chats[chatHistory.selectedIndex].messages || []; @@ -58,7 +53,6 @@ const InputOutputBackend: React.FC = () => { // When creating a new chat and no messages exist yet, set default messages addMessage("system", systemMessage) addMessage("assistant", "Hello! How can I help you?") - console.log(systemMessage) } }, [chatHistory, chatHistory.selectedIndex, systemMessage]); @@ -82,13 +76,12 @@ const InputOutputBackend: React.FC = () => { const password = localStorage.getItem("accountPassword") if (username && password && chatHistoryTriggered) { changeHistory(username, password, chatHistory) - console.log("changed history in backend") } }, [chatHistory]) + //#region functions const getWeatherHere = async () => { setWeatherData(await getWeather({ "unit_type": preferredMeasurement, "city": localStorage.getItem("weatherInfo") || "New York" })) - console.log("Got the Data!") setWeatherTriggered(true) } @@ -99,15 +92,12 @@ const InputOutputBackend: React.FC = () => { const tempChatHistory = await getHistory(username, password) if (tempChatHistory && typeof tempChatHistory == "object") { setChatHistory(tempChatHistory) - console.log("got history from backend") } } setChatHistoryTriggered(true) } - + //#region system-prompt useEffect(() => { - console.log("creating system prompt") - console.log(weatherData) const measurementString = (preferredMeasurement == "Metric") ? "All measurements follow the metric system. Refuse to use any other measurement system." @@ -123,17 +113,15 @@ const InputOutputBackend: React.FC = () => { These are the currently newest Weather infos for the region. Only for the case when the user asks about anything weather related, you can use the following data to help the user: ${weatherData}. If there is nothing there say there is no data` : `You are a helpful assistant.`; - console.log(newSystemMessage) setSystemMessage(newSystemMessage) }, [preferredCurrency, preferredLanguage, timeFormat, preferredMeasurement, timeZone, dateFormat, myBoolean, weatherTriggered]); useEffect(() => { const messageIndex = 0 // system prompt is the first so index 0 updateMessage(messageIndex, systemMessage) - console.log(messages) }, [systemMessage]) - + //#region more variables and functions const conversationRef = useRef(null) const [copyClicked, setCopyClicked] = useState(false) const [accessToken, setAccessToken] = useState("") @@ -144,7 +132,7 @@ const InputOutputBackend: React.FC = () => { const [isRecording, setIsRecording] = useState(false) const mediaRecorderRef = useRef(null) const audioChunks = useRef([]) - + //#region chat functions useEffect(() => { getNewToken() @@ -207,7 +195,6 @@ const InputOutputBackend: React.FC = () => { if (event.data == "error") { console.log("Error getting ai message.") } else { - console.log("Received data:", data); editLastMessage(data.response) } } @@ -266,7 +253,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 +296,7 @@ const InputOutputBackend: React.FC = () => { stopRecording(); } }; - + //#region chat buttons const handleStopClick = () => { endGetWorker() getNewToken() @@ -317,20 +304,20 @@ const InputOutputBackend: React.FC = () => { } const handleResendClick = () => { - const temporary_message = messages[messages.length - 2]['content'] - const updatedMessages = messages.slice(0, -2) - setMessages(updatedMessages) + const msg = chatHistory.chats[chatHistory.selectedIndex].messages + const lastUserMessage = msg[msg.length-2].content + msg.splice(msg.length-2,2) endGetWorker() getNewToken() setInputDisabled(false) - handleSendClick(temporary_message, true) + handleSendClick(lastUserMessage, true) } const handleEditClick = () => { - const newestMessage = messages[messages.length - 2].content - setInputMessage(newestMessage) - const updatedMessages = messages.slice(0, messages.length - 2) - setMessages(updatedMessages) + const msg = chatHistory.chats[chatHistory.selectedIndex].messages + const lastUserMessage = msg[msg.length-2].content + setInputMessage(lastUserMessage) + msg.splice(msg.length-2,2) endGetWorker() getNewToken() setInputDisabled(false) @@ -356,7 +343,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..387c38d 100644 --- a/app/backend/threads/GetWorker.ts +++ b/app/backend/threads/GetWorker.ts @@ -1,37 +1,30 @@ 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; - - console.log(apiURL.href) + const apiURL = new URL("http://localhost:5000/interstellar_ai/api/ai_get?access_token=" + accesstoken); + apiURL.hostname = windownameGlobal; // Set the hostname 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..6c84552 100644 --- a/app/backend/threads/PostWorker.ts +++ b/app/backend/threads/PostWorker.ts @@ -1,31 +1,30 @@ 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"); + apiURL.hostname = windowname; // Set the hostname for the API request - console.log(apiURL.href) + // 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..dbf070e 100644 --- a/app/backend/weather.ts +++ b/app/backend/weather.ts @@ -1,23 +1,28 @@ 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); + 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..b73ff61 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 b0b23aa..2ce5d27 100644 --- a/app/components/History.tsx +++ b/app/components/History.tsx @@ -1,96 +1,106 @@ import React, { useState } from 'react'; -import { useChatHistory } from '../hooks/useChatHistory'; +import { setSelectedIndex, 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, 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 + }; - 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 + }; + // 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 + + // Set the updated chat history + setChatHistory(copyChats); + }; + return (
) : ( )} @@ -120,4 +130,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..93ffa1b 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) || ""; } @@ -33,31 +34,33 @@ export function importSettings(jsonData: string): void { }); } - console.log("Settings imported successfully!"); } catch (error) { console.error("Invalid JSON data:", error); } } +// 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..eb1e204 100644 --- a/app/hooks/useChatHistory.tsx +++ b/app/hooks/useChatHistory.tsx @@ -1,68 +1,94 @@ -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 + (newState: GlobalChatHistory) => void, // Function to set global state +] => { + const [state, setState] = useState(globalChatHistory); // Local state initialized with global state useEffect(() => { - console.log("help", 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); + }; + }, []); - const setSelectedIndex = (index: number) => { - setGlobalState({...state,selectedIndex:index}) - } + + + // Return the current state and action functions + return [state, setGlobalState]; +} +// Function to set the selected chat index +export const setSelectedIndex = (index: number) => { + setGlobalState({ ...globalChatHistory, selectedIndex: index }); // Update the global state +} - const updateMessage = (messageIndex: number, newContent: string) => { - const updatedChats = [...state.chats] - const chatIndex = globalChatHistory.selectedIndex - if (chatIndex >= 0 && chatIndex < updatedChats.length) { - const updatedMessages = [...updatedChats[chatIndex].messages] - if (messageIndex >= 0 && messageIndex < updatedMessages.length) { - updatedMessages[messageIndex] = { ...updatedMessages[messageIndex], content: newContent } - updatedChats[chatIndex] = { ...updatedChats[chatIndex], messages: updatedMessages } - setGlobalState({...state, chats: updatedChats}) - } +// Function to update a specific message in the current chat +export const updateMessage = (messageIndex: number, newContent: string) => { + const updatedChats = [...globalChatHistory.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]; // Copy messages of the selected chat + + // Check if the message index is valid + if (messageIndex >= 0 && messageIndex < updatedMessages.length) { + // 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({ ...globalChatHistory, chats: updatedChats }); // Set the updated global state } } - - return [state, setSelectedIndex, setGlobalState, updateMessage] -} \ No newline at end of file +} diff --git a/app/layout.tsx b/app/layout.tsx index 1d7d206..af6b1f4 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,18 @@ 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 ( {metadata.title} - {/* Tried adding the favicon here */} diff --git a/app/page.tsx b/app/page.tsx index 3c77911..f2f3079 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' && } {view === 'Credits' && }
- +
); }; diff --git a/public/pdf/documentation.pdf b/public/pdf/documentation.pdf deleted file mode 100644 index 2ad3a1b..0000000 Binary files a/public/pdf/documentation.pdf and /dev/null differ 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,