forked from React-Group/interstellar_ai
Backend and frontend tweaks #16
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,6 +115,9 @@ const InputOutputBackend: React.FC = () => {
|
||||||
setMessages(previous => [...previous,{role,content}])
|
setMessages(previous => [...previous,{role,content}])
|
||||||
}
|
}
|
||||||
const handleSendClick = (inputValue: string) => {
|
const handleSendClick = (inputValue: string) => {
|
||||||
|
if (inputValue != "") {
|
||||||
|
if (!inputDisabled) {
|
||||||
|
setInputDisabled(true)
|
||||||
if (postWorkerRef.current) {
|
if (postWorkerRef.current) {
|
||||||
addMessage("user", inputValue)
|
addMessage("user", inputValue)
|
||||||
console.log("input:",inputValue);
|
console.log("input:",inputValue);
|
||||||
|
@ -116,6 +125,8 @@ const InputOutputBackend: React.FC = () => {
|
||||||
startGetWorker()
|
startGetWorker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleMicClick = () => {
|
const handleMicClick = () => {
|
||||||
// do stuff
|
// do stuff
|
||||||
|
@ -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>
|
|
||||||
<li>
|
|
||||||
<button onClick={() => onViewChange('Documentation')} className="header-button">Documentation</button>
|
|
||||||
</li>
|
|
||||||
{showToggle && showHistoryModelsToggle && (
|
{showToggle && showHistoryModelsToggle && (
|
||||||
<li>
|
<button onClick={toggleDivs} className="nav-btn">
|
||||||
<button onClick={toggleDivs} className="header-button">
|
|
||||||
{showDivs ? 'Hide History/Models' : 'Show History/Models'}
|
{showDivs ? 'Hide History/Models' : 'Show History/Models'}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
|
||||||
)}
|
)}
|
||||||
</ul>
|
</nav>
|
||||||
|
{/* <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,22 +4,26 @@ 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 (!inputDisabled) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
onSendClick(inputValue); // Call the function passed via props
|
onSendClick(inputValue); // Call the function passed via props
|
||||||
setInputValue(''); // Optionally clear input after submission
|
setInputValue(''); // Optionally clear input after submission
|
||||||
event.preventDefault(); // Prevent default action (e.g., form submission)
|
event.preventDefault(); // Prevent default action (e.g., form submission)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
14
py/api.py
14
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,9 +25,13 @@ 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)
|
||||||
|
|
||||||
|
if access_token not in self.ai_response:
|
||||||
self.ai_response[access_token] = ""
|
self.ai_response[access_token] = ""
|
||||||
return jsonify({'status': 200, 'access_token': 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():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
@ -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…
Reference in a new issue