forked from React-Group/interstellar_ai
		
	Merge pull request 'Backend and frontend tweaks' (#16) from React-Group/interstellar_ai:main into main
Reviewed-on: https://interstellardevelopment.org/code/code/sageTheDm/interstellar_ai/pulls/16
This commit is contained in:
		
						commit
						9577d2f252
					
				
					 9 changed files with 181 additions and 55 deletions
				
			
		|  | @ -3,7 +3,6 @@ import React, { useEffect, useRef, useState } from "react"; | ||||||
| import ConversationFrontend from "../components/ConversationFrontend"; | import ConversationFrontend from "../components/ConversationFrontend"; | ||||||
| import InputFrontend from "../components/InputFrontend"; | import InputFrontend from "../components/InputFrontend"; | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| import { log } from 'console'; |  | ||||||
| 
 | 
 | ||||||
| const InputOutputBackend: React.FC = () => { | const InputOutputBackend: React.FC = () => { | ||||||
|   type Message = { |   type Message = { | ||||||
|  | @ -16,6 +15,7 @@ const InputOutputBackend: React.FC = () => { | ||||||
|   const getWorkerRef = useRef<Worker | null>(null) |   const getWorkerRef = useRef<Worker | null>(null) | ||||||
|   const [messages, setMessages] = useState<Message[]>([{role:"assistant", content:"Hello! How can I help you?"}]) |   const [messages, setMessages] = useState<Message[]>([{role:"assistant", content:"Hello! How can I help you?"}]) | ||||||
|   const [liveMessage, setLiveMessage] = useState("") |   const [liveMessage, setLiveMessage] = useState("") | ||||||
|  |   const [inputDisabled, setInputDisabled] = useState(false) | ||||||
| 
 | 
 | ||||||
|   console.log(messages); |   console.log(messages); | ||||||
|    |    | ||||||
|  | @ -37,8 +37,10 @@ const InputOutputBackend: React.FC = () => { | ||||||
|     postWorkerRef.current.onmessage = (event) => { |     postWorkerRef.current.onmessage = (event) => { | ||||||
|       const status = event.data.status |       const status = event.data.status | ||||||
|       if (status == 200) { |       if (status == 200) { | ||||||
|  |         setInputDisabled(false) | ||||||
|         endGetWorker() |         endGetWorker() | ||||||
|       } else if (status == 500) { |       } else if (status == 500) { | ||||||
|  |         setInputDisabled(false) | ||||||
|         if (getWorkerRef.current) { |         if (getWorkerRef.current) { | ||||||
|           addMessage("assistant", "There was an Error with the AI response") |           addMessage("assistant", "There was an Error with the AI response") | ||||||
|           getWorkerRef.current.postMessage("terminate") |           getWorkerRef.current.postMessage("terminate") | ||||||
|  | @ -86,11 +88,15 @@ const InputOutputBackend: React.FC = () => { | ||||||
|     if (getWorkerRef.current) { |     if (getWorkerRef.current) { | ||||||
|       getWorkerRef.current.postMessage({action:"terminate"}) |       getWorkerRef.current.postMessage({action:"terminate"}) | ||||||
|       getWorkerRef.current.terminate() |       getWorkerRef.current.terminate() | ||||||
|  |       getWorkerRef.current = null | ||||||
|       console.log(messages); |       console.log(messages); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const editLastMessage = (newContent: string) => { |   const editLastMessage = (newContent: string) => { | ||||||
|  |     if (newContent == "") { | ||||||
|  |       newContent = "Generating answer..." | ||||||
|  |     } | ||||||
|   setMessages((prevMessages) => { |   setMessages((prevMessages) => { | ||||||
|     const updatedMessages = prevMessages.slice(); // Create a shallow copy of the current messages
 |     const updatedMessages = prevMessages.slice(); // Create a shallow copy of the current messages
 | ||||||
|     if (updatedMessages.length > 0) { |     if (updatedMessages.length > 0) { | ||||||
|  | @ -109,11 +115,16 @@ const InputOutputBackend: React.FC = () => { | ||||||
|     setMessages(previous => [...previous,{role,content}]) |     setMessages(previous => [...previous,{role,content}]) | ||||||
|   }   |   }   | ||||||
|   const handleSendClick = (inputValue: string) => { |   const handleSendClick = (inputValue: string) => { | ||||||
|     if (postWorkerRef.current) { |     if (inputValue != "") { | ||||||
|       addMessage("user", inputValue) |       if (!inputDisabled) { | ||||||
|       console.log("input:",inputValue); |         setInputDisabled(true) | ||||||
|       postWorkerRef.current.postMessage({messages:[...messages, { role: "user", content: inputValue }], ai_model:"phi3.5", access_token:accessToken}) |         if (postWorkerRef.current) { | ||||||
|       startGetWorker() |           addMessage("user", inputValue) | ||||||
|  |           console.log("input:",inputValue); | ||||||
|  |           postWorkerRef.current.postMessage({messages:[...messages, { role: "user", content: inputValue }], ai_model:"phi3.5", access_token:accessToken}) | ||||||
|  |           startGetWorker() | ||||||
|  |         } | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -145,6 +156,7 @@ const InputOutputBackend: React.FC = () => { | ||||||
|         message="" |         message="" | ||||||
|         onSendClick={handleSendClick} |         onSendClick={handleSendClick} | ||||||
|         onMicClick={handleMicClick} |         onMicClick={handleMicClick} | ||||||
|  |         inputDisabled={inputDisabled} | ||||||
|       /> |       /> | ||||||
|     </div> |     </div> | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  | @ -29,6 +29,6 @@ const fetchData = () => { | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|             console.log('Error fetching data:', error); |             console.log('Error fetching data:', error); | ||||||
|             postMessage({error:"failed fetching data"}) |             postMessage({error:"failed fetching data"}) | ||||||
|          |             setTimeout(() => fetchData(),1000) | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  | @ -2,7 +2,7 @@ import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| onmessage = (e) => { | onmessage = (e) => { | ||||||
|     const { messages = [{ role: "assistant", content: "Hello! How can I help you?" }], ai_model = "phi3.5", access_token } = e.data |     const { messages = [{ role: "assistant", content: "Hello! How can I help you?" }], ai_model = "phi3.5", access_token } = e.data | ||||||
|     messages.unshift({ role: "system", content: "You are a Helpful assistant" }) |     messages.unshift({ role: "system", content: "You are a Helpful assistant. you give short answers" }) | ||||||
| 
 | 
 | ||||||
|     const Message = { |     const Message = { | ||||||
|         messages: messages, |         messages: messages, | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| // Header.tsx
 | // Header.tsx
 | ||||||
| import React from 'react'; | import React, { useState } from 'react'; | ||||||
| import Login from './Login'; | import Login from './Login'; | ||||||
| 
 | 
 | ||||||
| interface HeaderProps { | interface HeaderProps { | ||||||
|  | @ -11,29 +11,32 @@ interface HeaderProps { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Header: React.FC<HeaderProps> = ({ onViewChange, showDivs, toggleDivs, showHistoryModelsToggle, showToggle }) => { | const Header: React.FC<HeaderProps> = ({ onViewChange, showDivs, toggleDivs, showHistoryModelsToggle, showToggle }) => { | ||||||
|  |   const [menuOpen, setMenuOpen] = useState(false) | ||||||
|  |    | ||||||
|  |   const toggleMenu = () => { | ||||||
|  |     setMenuOpen(!menuOpen) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <header> |       <header> | ||||||
|         <ul> |         <div className={`hamburger ${menuOpen ? "open" : ""}`} onClick={toggleMenu}> | ||||||
|           <li> |           <span></span> | ||||||
|             <button onClick={() => onViewChange('AI')} className="header-button header-logo"> |           <span></span> | ||||||
|               <img src="/img/logo.png" alt="logo" className="header-logo" /> |           <span></span> | ||||||
|             </button> |         </div> | ||||||
|           </li> |         <nav className={`nav-links ${menuOpen ? "active":""}`}> | ||||||
|           <li> |             <button onClick={() => onViewChange('FAQ')} className="nav-btn">FAQ</button> | ||||||
|             <button onClick={() => onViewChange('FAQ')} className="header-button">FAQ</button> |             <button onClick={() => onViewChange('Documentation')} className="nav-btn">Documentation</button> | ||||||
|           </li> |             {showToggle && showHistoryModelsToggle && ( | ||||||
|           <li> |               <button onClick={toggleDivs} className="nav-btn"> | ||||||
|             <button onClick={() => onViewChange('Documentation')} className="header-button">Documentation</button> |  | ||||||
|           </li> |  | ||||||
|           {showToggle && showHistoryModelsToggle && ( |  | ||||||
|             <li> |  | ||||||
|               <button onClick={toggleDivs} className="header-button"> |  | ||||||
|                 {showDivs ? 'Hide History/Models' : 'Show History/Models'} |                 {showDivs ? 'Hide History/Models' : 'Show History/Models'} | ||||||
|               </button> |               </button> | ||||||
|             </li> |             )} | ||||||
|           )} |           </nav> | ||||||
|         </ul> |           {/* <button onClick={() => onViewChange('AI')} className="header-button header-logo"> | ||||||
|  |             <img src="/img/logo.png" alt="logo" className="header-logo" /> | ||||||
|  |           </button> */} | ||||||
|       <Login /> |       <Login /> | ||||||
|       </header> |       </header> | ||||||
|     </> |     </> | ||||||
|  |  | ||||||
|  | @ -4,21 +4,25 @@ interface InputProps { | ||||||
|   message: string; |   message: string; | ||||||
|   onSendClick: (message: string) => void; |   onSendClick: (message: string) => void; | ||||||
|   onMicClick: () => void; |   onMicClick: () => void; | ||||||
|  |   inputDisabled:boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const InputFrontend = React.forwardRef<HTMLDivElement, InputProps>( | const InputFrontend = React.forwardRef<HTMLDivElement, InputProps>( | ||||||
|   ({ message, onSendClick, onMicClick }, ref: ForwardedRef<HTMLDivElement>) => { |   ({ message, onSendClick, onMicClick, inputDisabled }, ref: ForwardedRef<HTMLDivElement>) => { | ||||||
|     const [inputValue, setInputValue] = useState(''); |     const [inputValue, setInputValue] = useState(''); | ||||||
|      |      | ||||||
|  | 
 | ||||||
|     const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { |     const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||||
|       setInputValue(e.target.value); |       setInputValue(e.target.value); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => { |     const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => { | ||||||
|       if (event.key === 'Enter') { |       if (!inputDisabled) { | ||||||
|         onSendClick(inputValue); // Call the function passed via props
 |         if (event.key === 'Enter') { | ||||||
|         setInputValue(''); // Optionally clear input after submission
 |           onSendClick(inputValue); // Call the function passed via props
 | ||||||
|         event.preventDefault(); // Prevent default action (e.g., form submission)
 |           setInputValue(''); // Optionally clear input after submission
 | ||||||
|  |           event.preventDefault(); // Prevent default action (e.g., form submission)
 | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -32,7 +36,7 @@ const InputFrontend = React.forwardRef<HTMLDivElement, InputProps>( | ||||||
|           onChange={handleInputChange} |           onChange={handleInputChange} | ||||||
|           onKeyDown={handleKeyDown} |           onKeyDown={handleKeyDown} | ||||||
|         /> |         /> | ||||||
|         <button type="button" onClick={() => onSendClick(inputValue)}> |         <button type="button" onClick={() => onSendClick(inputValue)} disabled={inputDisabled?true:false}> | ||||||
|           <img src="/img/send.svg" alt="send" /> |           <img src="/img/send.svg" alt="send" /> | ||||||
|         </button> |         </button> | ||||||
|         <button type="button" onClick={onMicClick}> |         <button type="button" onClick={onMicClick}> | ||||||
|  |  | ||||||
|  | @ -10,29 +10,84 @@ header { | ||||||
|     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); |     box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | ||||||
|     z-index: 1000; |     z-index: 1000; | ||||||
|     font-family: var(--font-family); |     font-family: var(--font-family); | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: space-between; | ||||||
|  |     align-items: center; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| header li { | .nav-links{ | ||||||
|     display: inline-block; |     display: flex; | ||||||
|     margin: 0 15px; |     gap: 15px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| header img { | .nav-btn{ | ||||||
|     height: 2em; |     background: transparent; | ||||||
|     vertical-align: middle; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| header a,  |  | ||||||
| header li button { |  | ||||||
|     color: var(--header-text-color); /* Use the new header text color */ |  | ||||||
|     text-decoration: none; |  | ||||||
|     transition: color 0.3s; |  | ||||||
|     border: none; |     border: none; | ||||||
|     background-color: transparent; |     cursor: pointer; | ||||||
|     font-size: 1em; |     /* color */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| header a:hover, | .nav-btn:hover{ | ||||||
| header li button:hover { |     /* color */ | ||||||
|     color: var(--input-button-color); /* Keep the hover color */ | } | ||||||
|  | 
 | ||||||
|  | .hamburger{ | ||||||
|  |     display: none; | ||||||
|  |     flex-direction: column; | ||||||
|  |     cursor: pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hamburger span{ | ||||||
|  |     width: 25px; | ||||||
|  |     height: 3px; | ||||||
|  |     background-color: var(--header-text-color); | ||||||
|  |     margin: 4px; | ||||||
|  |     transition: 0.3s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hamburger.open span:nth-child(1){ | ||||||
|  |     transform: rotate(45deg) translate(5px, 5px); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hamburger.open span:nth-child(2){ | ||||||
|  |     opacity: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hamburger.open span:nth-child(3){ | ||||||
|  |     transform: rotate(-45deg) translate(5px, -5px); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .header-button{ | ||||||
|  |      | ||||||
|  | } | ||||||
|  | .header-button img{ | ||||||
|  |     height: 8vh; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (max-width:768px) { | ||||||
|  |     .nav-links{ | ||||||
|  |         display: none; | ||||||
|  |         position: absolute; | ||||||
|  |         top: 60px; | ||||||
|  |         right: 0; | ||||||
|  |         /* background color */ | ||||||
|  |         width: 100%; | ||||||
|  |         flex-direction: column; | ||||||
|  |         align-items: flex-start; | ||||||
|  |         padding: 10px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .nav-links.active{ | ||||||
|  |         display: flex; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .nav-btn{ | ||||||
|  |         width: 100%; | ||||||
|  |         text-align: center; | ||||||
|  |         padding: 10px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .hamburger{ | ||||||
|  |         display: flex; | ||||||
|  |     } | ||||||
| } | } | ||||||
							
								
								
									
										18
									
								
								py/api.py
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								py/api.py
									
										
									
									
									
								
							|  | @ -4,16 +4,18 @@ import secrets | ||||||
| import threading | import threading | ||||||
| from ai import AI | from ai import AI | ||||||
| from db import DB | from db import DB | ||||||
|  | from weather import Weather | ||||||
| from voice import VoiceRecognition | from voice import VoiceRecognition | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class API: | class API: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.crypt_size = 1 |         self.crypt_size = 4096 | ||||||
|         self.app = Flask(__name__) |         self.app = Flask(__name__) | ||||||
|         self.ai_response = {} |         self.ai_response = {} | ||||||
|         self.ai = AI() |         self.ai = AI() | ||||||
|         self.db = DB() |         self.db = DB() | ||||||
|  |         self.weather = Weather() | ||||||
|         self.voice = VoiceRecognition() |         self.voice = VoiceRecognition() | ||||||
|         self.db.load_database() |         self.db.load_database() | ||||||
|         self.ai_response_lock = threading.Lock() |         self.ai_response_lock = threading.Lock() | ||||||
|  | @ -23,8 +25,12 @@ class API: | ||||||
|         @self.app.route('/interstellar_ai/api/ai_create', methods=['GET']) |         @self.app.route('/interstellar_ai/api/ai_create', methods=['GET']) | ||||||
|         def create_ai(): |         def create_ai(): | ||||||
|             access_token = secrets.token_urlsafe(self.crypt_size) |             access_token = secrets.token_urlsafe(self.crypt_size) | ||||||
|             self.ai_response[access_token] = "" | 
 | ||||||
|             return jsonify({'status': 200, 'access_token': access_token}) |             if access_token not in self.ai_response: | ||||||
|  |                 self.ai_response[access_token] = "" | ||||||
|  |                 return jsonify({'status': 200, 'access_token': access_token}) | ||||||
|  | 
 | ||||||
|  |             return jsonify({'status': 401, 'error': 'An error occurred, please try again.'}) | ||||||
| 
 | 
 | ||||||
|         @self.app.route('/interstellar_ai/api/ai_send', methods=['POST']) |         @self.app.route('/interstellar_ai/api/ai_send', methods=['POST']) | ||||||
|         def send_ai(): |         def send_ai(): | ||||||
|  | @ -99,6 +105,12 @@ class API: | ||||||
| 
 | 
 | ||||||
|             return jsonify({'status': 401, 'response': "Invalid type"}) |             return jsonify({'status': 401, 'response': "Invalid type"}) | ||||||
| 
 | 
 | ||||||
|  |         @self.app.route('/interstellar_ai/api/weather', methods=['POST']) | ||||||
|  |         def get_weather(): | ||||||
|  |             unit_type = request.args.get('unit_type') | ||||||
|  |             city = request.args.get('city') | ||||||
|  |             return jsonify({'status': 200, 'response': self.weather.getweather(unit_type, city)}) | ||||||
|  | 
 | ||||||
|         self.app.run(debug=True, host='0.0.0.0', port=5000) |         self.app.run(debug=True, host='0.0.0.0', port=5000) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,3 +9,4 @@ SpeechRecognition | ||||||
| PocketSphinx | PocketSphinx | ||||||
| google-cloud-speech | google-cloud-speech | ||||||
| google-generativeai | google-generativeai | ||||||
|  | python-weather | ||||||
							
								
								
									
										39
									
								
								py/weather.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								py/weather.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | import python_weather | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Weather: | ||||||
|  |     @staticmethod | ||||||
|  |     async def getweather(unit_type, city): | ||||||
|  | 
 | ||||||
|  |         if unit_type == "imperial": | ||||||
|  |             unit_type = python_weather.IMPERIAL | ||||||
|  |         elif unit_type == "metric": | ||||||
|  |             unit_type = python_weather.METRIC | ||||||
|  | 
 | ||||||
|  |         async with python_weather.Client(unit=unit_type) as client: | ||||||
|  |             weather = await client.get(city) | ||||||
|  | 
 | ||||||
|  |             data = { | ||||||
|  |                 'temperature': weather.temperature, | ||||||
|  |                 'humidity': weather.humidity, | ||||||
|  |                 'unit': weather.unit, | ||||||
|  |                 'datetime': weather.datetime, | ||||||
|  |                 'coordinates': weather.coordinates, | ||||||
|  |                 'country': weather.country, | ||||||
|  |                 'daily_forecasts': weather.daily_forecasts, | ||||||
|  |                 'description': weather.description, | ||||||
|  |                 'feels_like': weather.feels_like, | ||||||
|  |                 'kind': weather.kind, | ||||||
|  |                 'local_population': weather.local_population, | ||||||
|  |                 'locale': weather.locale, | ||||||
|  |                 'location': weather.location, | ||||||
|  |                 'precipitation': weather.precipitation, | ||||||
|  |                 'pressure': weather.pressure, | ||||||
|  |                 'region': weather.region, | ||||||
|  |                 'ultraviolet': weather.ultraviolet, | ||||||
|  |                 'visibility': weather.visibility, | ||||||
|  |                 'wind_direction': weather.wind_direction, | ||||||
|  |                 'wind_speed': weather.wind_speed, | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return data | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue