From 7e69674978ac27e9e97ecab6431fd1de8addefd9 Mon Sep 17 00:00:00 2001 From: YasinOnm08 Date: Mon, 23 Sep 2024 11:11:45 +0200 Subject: [PATCH 1/7] multithread trial 2 --- app/backend/InputOutputHandler.tsx | 159 +++++++++++++++++------------ app/backend/ProcessAPI.js | 51 --------- app/backend/threads/GetWorker.js | 29 ++++++ app/backend/threads/PostWorker.js | 15 +++ 4 files changed, 140 insertions(+), 114 deletions(-) delete mode 100644 app/backend/ProcessAPI.js create mode 100644 app/backend/threads/GetWorker.js create mode 100644 app/backend/threads/PostWorker.js diff --git a/app/backend/InputOutputHandler.tsx b/app/backend/InputOutputHandler.tsx index ed09bb4..b7bea84 100644 --- a/app/backend/InputOutputHandler.tsx +++ b/app/backend/InputOutputHandler.tsx @@ -2,78 +2,111 @@ import React, { useEffect, useRef, useState } from "react"; import ConversationFrontend from "../components/ConversationFrontend"; import InputFrontend from "../components/InputFrontend"; - -const handleMicClick = () => { - console.log('Mic clicked!'); - // Do something when the mic button is clicked -}; - - -const handleResendClick = () => { - console.log('Resend button clicked'); - // Handle resend action -}; - -const handleEditClick = () => { - console.log('Edit button clicked'); - // Handle edit action -}; - -const handleCopyClick = () => { - console.log('Copy button clicked'); - // Handle copy action -}; +import { error, log } from "console"; +import axios from "axios"; const InputOutputBackend: React.FC = () => { - const [accessToken, setAccessToken] = useState("") - const workerRef = useRef(null) - type Message = { - role: string - content: string - } + const [accessToken, setAccessToken] = useState("") + const postWorkerRef = useRef(null) + const getWorkerRef = useRef(null) + const [messages, setMessages] = useState([{role:"system", content:"You are a helpful assistant"}]) + const [liveMessage, setLiveMessage] = useState("") - const handleSendClick = (message:string) => { - var system:Message = {role:"system" ,content:"You are a helpful assistant."} - - addMessage("user", message); - console.log("added User Message") + useEffect(() => { + axios.get("http://localhost:5000/interstellar/api/ai_create") + .then(response => { + setAccessToken(response.data.access_token) + }) + .catch(error => { + console.log("error:", error.message); - HandlePostRequest([...messages, { role: "user", content: message }], "phi3.5", system); - }; + }) - const [messages, setMessages] = useState([ - { role:"assistant", content:'Hello. I\'m Your AI Virtual Assistant' } - ]); + postWorkerRef.current = new Worker(new URL("./threads/PostWorker.js", import.meta.url)) - const addMessage = (role:string ,content: string) => { - setMessages((prevMessages) => [...prevMessages, {role,content}]); - }; - - - - useEffect(() => { - workerRef.current = new Worker(new URL("./ProcessAPI.js", import.meta.url)) - workerRef.current.postMessage({}) - workerRef.current.onmessage = (e) => { - setAccessToken(e.data) - } - - return () => { - if (workerRef.current) { - workerRef.current.terminate() - } - } - },[]) - - const HandlePostRequest = (messages: Message[], ai_model: string, system_prompt: Message) => { - if (workerRef.current) { - workerRef.current.postMessage({ functionName: "postRequest", access_token: accessToken, messages: messages, ai_model: ai_model, system_prompt: system_prompt }) - workerRef.current.onmessage = (e) => { - addMessage("assistant",e.data) - } + postWorkerRef.current.onmessage = (event) => { + const status = event.data.status + if (status == 200) { + endGetWorker() + } else if (status == 500) { + if (getWorkerRef.current) { + addMessage("assistant", "There was an Error with the AI response") + getWorkerRef.current.postMessage("terminate") + getWorkerRef.current.terminate() } + } } + return () => { + if (postWorkerRef.current) { + postWorkerRef.current.terminate() + } + if (getWorkerRef.current) { + getWorkerRef.current.postMessage("terminate") + getWorkerRef.current.terminate() + } + } + },[]) + + const startGetWorker = () => { + if (!getWorkerRef.current) { + getWorkerRef.current = new Worker(new URL("./threads/GetWorker.js", import.meta.url)) + + getWorkerRef.current.postMessage("start") + + getWorkerRef.current.onmessage = (event) => { + const data = event.data + + if (data.error) { + setLiveMessage("error getting AI response: "+ data.error) + } else { + console.log("Received data:", data); + setLiveMessage(data) + } + } + + getWorkerRef.current.onerror = (error) => { + console.error("Worker error:", error) + } + } + } + + const endGetWorker = () => { + if (getWorkerRef.current) { + addMessage("assistant", liveMessage) + getWorkerRef.current.postMessage("terminate") + getWorkerRef.current.terminate() + } + } + + const addMessage = (role: string, content: string) => { + setMessages(previous => [...previous,{role,content}]) + } + const handleSendClick = (inputValue: string) => { + if (postWorkerRef.current) { + addMessage("user", inputValue) + console.log("input:",inputValue); + postWorkerRef.current.postMessage({messages:messages, ai_model:"phi3.5", access_token:accessToken}) + startGetWorker() + } + } + + const handleMicClick = () => { + // do stuff + } + + const handleResendClick = () => { + // do stuff + } + + const handleEditClick = () => { + // do stuff + } + + const handleCopyClick = () => { + // do stuff + } + return (
{ - messageComplete:boolean = false - while(!messageComplete) - axios.get('https://localhost:5000/interstellar/api/ai_get?access_token=' + access_token) - .then(Response => { - postMessage(Response.data.response) - if (Response.data.status == 200) { - messageComplete = true - } - }).catch(error => { - console.error("Error with GET response request:", error) - }) - } - - switch (functionName) { - case "getAccess": - console.log("getting access...") - axios.get('https://localhost:5000/interstellar/api/ai_create') - .then(Response => { - postMessage(Response.data.access_token) - }).catch(error => { - console.error("Error with GET Token request:", error) - }) - break - case "postRequest": - messages.unshift(system_prompt) - console.log("sending...") - console.log(messages) - axios.post('https://localhost:5000/interstellar/api/ai_send', data) - .then(Response => { - getResponse() - }).catch(error => { - console.error("Error:", error) - }) - break - } - - -} \ No newline at end of file diff --git a/app/backend/threads/GetWorker.js b/app/backend/threads/GetWorker.js new file mode 100644 index 0000000..f8a4668 --- /dev/null +++ b/app/backend/threads/GetWorker.js @@ -0,0 +1,29 @@ +import axios from "axios"; + +let shouldRun = false +onmessage = (event) => { + if (event.data === "start") { + shouldRun = true + fetchData() + } else if (event.date === "terminate") { + shouldRun = false + } +} + +const fetchData = () => { + if (!shouldRun) return + + const apiURL = "http://localhost:5000/interstellar/api/ai_get" + + axios.get(apiURL) + .then(response => { + const data = response.data.response + postMessage(data) + setTimeout(fetchData,500) + }) + .catch(error => { + console.log('Error fetching data:', error); + postMessage({error:"failed fetching data"}) + + }) +} \ No newline at end of file diff --git a/app/backend/threads/PostWorker.js b/app/backend/threads/PostWorker.js new file mode 100644 index 0000000..56c2201 --- /dev/null +++ b/app/backend/threads/PostWorker.js @@ -0,0 +1,15 @@ +import axios from "axios"; + +onmessage = (e) => { + const { messages = [{ role: "system", content: "You are a helpful assistant" }], ai_model = "phi3.5", access_token } = e.data + + axios.post("http://localhost:5000/interstellar/api/ai_send") + .then(response => { + const status = response.data.status + postMessage({status}) + }) + .catch(error => { + console.log("Error calling API:", error) + postMessage({status:500}) + }) +} \ No newline at end of file From 6605ed126fbb011ae2f12d60a7312fbcee6d4bbf Mon Sep 17 00:00:00 2001 From: Patrick_Pluto Date: Mon, 23 Sep 2024 14:46:17 +0200 Subject: [PATCH 2/7] WebView --- py/requirements.txt | 3 ++- py/webapp.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 py/webapp.py diff --git a/py/requirements.txt b/py/requirements.txt index bd93ad7..8a79dc1 100644 --- a/py/requirements.txt +++ b/py/requirements.txt @@ -4,4 +4,5 @@ ollama mistralai openai anthropic -pyOpenSSL \ No newline at end of file +pyOpenSSL +pywebview \ No newline at end of file diff --git a/py/webapp.py b/py/webapp.py new file mode 100644 index 0000000..6ad4725 --- /dev/null +++ b/py/webapp.py @@ -0,0 +1,3 @@ +import webview +webview.create_window('Interstellar AI', 'http://localhost:3000') +webview.start() From f587864d3ca0dd8dea9a6dda99bc1c4764350f3d Mon Sep 17 00:00:00 2001 From: YasinOnm08 Date: Mon, 23 Sep 2024 14:54:13 +0200 Subject: [PATCH 3/7] multithread trial 3 --- app/backend/InputOutputHandler.tsx | 15 +++++++++------ app/backend/threads/GetWorker.js | 19 ++++++++++++------- app/backend/threads/PostWorker.js | 19 ++++++++++++++++--- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/app/backend/InputOutputHandler.tsx b/app/backend/InputOutputHandler.tsx index b7bea84..ccd3208 100644 --- a/app/backend/InputOutputHandler.tsx +++ b/app/backend/InputOutputHandler.tsx @@ -2,20 +2,22 @@ import React, { useEffect, useRef, useState } from "react"; import ConversationFrontend from "../components/ConversationFrontend"; import InputFrontend from "../components/InputFrontend"; -import { error, log } from "console"; import axios from "axios"; +import { log } from 'console'; const InputOutputBackend: React.FC = () => { const [accessToken, setAccessToken] = useState("") const postWorkerRef = useRef(null) const getWorkerRef = useRef(null) - const [messages, setMessages] = useState([{role:"system", content:"You are a helpful assistant"}]) + const [messages, setMessages] = useState([{role:"assistant", content:"Hello! How can I help you?"}]) const [liveMessage, setLiveMessage] = useState("") useEffect(() => { + console.log("getting access"); axios.get("http://localhost:5000/interstellar/api/ai_create") .then(response => { - setAccessToken(response.data.access_token) + setAccessToken(response.data.access_token) + console.log(response.data.access_token); }) .catch(error => { console.log("error:", error.message); @@ -52,12 +54,12 @@ const InputOutputBackend: React.FC = () => { if (!getWorkerRef.current) { getWorkerRef.current = new Worker(new URL("./threads/GetWorker.js", import.meta.url)) - getWorkerRef.current.postMessage("start") + getWorkerRef.current.postMessage({ action: "start", access_token:accessToken}) getWorkerRef.current.onmessage = (event) => { const data = event.data - if (data.error) { + if (event.data == "error") { setLiveMessage("error getting AI response: "+ data.error) } else { console.log("Received data:", data); @@ -74,8 +76,9 @@ const InputOutputBackend: React.FC = () => { const endGetWorker = () => { if (getWorkerRef.current) { addMessage("assistant", liveMessage) - getWorkerRef.current.postMessage("terminate") + getWorkerRef.current.postMessage({action:"terminate"}) getWorkerRef.current.terminate() + console.log(messages); } } diff --git a/app/backend/threads/GetWorker.js b/app/backend/threads/GetWorker.js index f8a4668..a89e02b 100644 --- a/app/backend/threads/GetWorker.js +++ b/app/backend/threads/GetWorker.js @@ -2,24 +2,29 @@ import axios from "axios"; let shouldRun = false onmessage = (event) => { - if (event.data === "start") { + const { action, access_token } = event.data + if (action === "start") { shouldRun = true - fetchData() - } else if (event.date === "terminate") { + fetchData(access_token) + } else if (action === "terminate") { shouldRun = false } } -const fetchData = () => { +console.log('starting get loop'); + +const fetchData = (access_token) => { if (!shouldRun) return - const apiURL = "http://localhost:5000/interstellar/api/ai_get" + + const apiURL = "http://localhost:5000/interstellar/api/ai_get?access_token="+access_token axios.get(apiURL) .then(response => { - const data = response.data.response + const data = response.data + console.log(data); postMessage(data) - setTimeout(fetchData,500) + setTimeout(fetchData,100) }) .catch(error => { console.log('Error fetching data:', error); diff --git a/app/backend/threads/PostWorker.js b/app/backend/threads/PostWorker.js index 56c2201..5d6ea63 100644 --- a/app/backend/threads/PostWorker.js +++ b/app/backend/threads/PostWorker.js @@ -1,12 +1,25 @@ import axios from "axios"; onmessage = (e) => { - const { messages = [{ role: "system", content: "You are a helpful assistant" }], 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 + + const promptedMessage = messages.unshift({role:"system", content:"You are a Helpful assistant"}) + + const Message = { + messages: promptedMessage, + ai_model: "phi3.5", + model_type:"local", + access_token:access_token + } + - axios.post("http://localhost:5000/interstellar/api/ai_send") + axios.post("http://localhost:5000/interstellar/api/ai_send",Message) .then(response => { const status = response.data.status - postMessage({status}) + console.log(status); + postMessage({ status }) + console.log('message posted'); + }) .catch(error => { console.log("Error calling API:", error) From 1a45c04b8855b90838318dbfb145a94b10e5c20a Mon Sep 17 00:00:00 2001 From: Patrick_Pluto Date: Mon, 23 Sep 2024 15:02:02 +0200 Subject: [PATCH 4/7] WebView better --- py/requirements.txt | 3 +++ py/webapp.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/py/requirements.txt b/py/requirements.txt index 8a79dc1..a5fe3e6 100644 --- a/py/requirements.txt +++ b/py/requirements.txt @@ -5,4 +5,7 @@ mistralai openai anthropic pyOpenSSL +pywebview +tkhtmlview +cefpython3 pywebview \ No newline at end of file diff --git a/py/webapp.py b/py/webapp.py index 6ad4725..e97750b 100644 --- a/py/webapp.py +++ b/py/webapp.py @@ -1,3 +1,3 @@ import webview -webview.create_window('Interstellar AI', 'http://localhost:3000') +webview.create_window('Hello world', 'http://localhost:3000') webview.start() From 065b241f19ab2edbecb0fcab411caad97ec5354f Mon Sep 17 00:00:00 2001 From: Patrick_Pluto Date: Mon, 23 Sep 2024 16:06:20 +0200 Subject: [PATCH 5/7] Update README.md --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/README.md b/README.md index e69de29..0efe25b 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,46 @@ +# Installation Guide + +In order to use this app, you need quite a lot of things, so buckle up. + +## Dependencies + +Install the following: + +https://ollama.com/download + +https://www.python.org/downloads/ + +https://nodejs.org/en/download/prebuilt-installer + +For Linux and NodeJS, see: + +https://nodejs.org/en/download/package-manager/all + +## Running the project for the first time + +In order to now actually run the program, you have to open a terminal in the project folder, then you can run: + +``` +npm install +cd py +pip install -R requirements.txt +``` + +In case you need a virtual environment however, we provide a custom install.sh file for GNU/Linux. + +``` +sh install.sh +``` + +From then on, you can run the project by: +1. Opening two terminal windows, one in the main project folder, and one in the py subfolder. +2. In the py subfolder, you will run: +``` +python3 api.py +``` +3. In the main project folder, you will run: +``` +npm run dev +``` +4. Open http://localhost:3000/ in your browser. +5. Enjoy! \ No newline at end of file From 8cc191dad1b56d50806bf3eee30691e608d24033 Mon Sep 17 00:00:00 2001 From: YasinOnm08 Date: Mon, 23 Sep 2024 16:34:55 +0200 Subject: [PATCH 6/7] live message! (limit:1) --- app/backend/InputOutputHandler.tsx | 31 ++++++++++++++++++++++++++---- app/backend/threads/GetWorker.js | 16 +++++++-------- app/backend/threads/PostWorker.js | 7 ++++--- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/app/backend/InputOutputHandler.tsx b/app/backend/InputOutputHandler.tsx index ccd3208..bf8a191 100644 --- a/app/backend/InputOutputHandler.tsx +++ b/app/backend/InputOutputHandler.tsx @@ -6,12 +6,20 @@ import axios from "axios"; import { log } from 'console'; const InputOutputBackend: React.FC = () => { + type Message = { + role: string + content:string + } + const [accessToken, setAccessToken] = useState("") const postWorkerRef = useRef(null) const getWorkerRef = useRef(null) - const [messages, setMessages] = useState([{role:"assistant", content:"Hello! How can I help you?"}]) + const [messages, setMessages] = useState([{role:"assistant", content:"Hello! How can I help you?"}]) const [liveMessage, setLiveMessage] = useState("") + console.log(messages); + + useEffect(() => { console.log("getting access"); axios.get("http://localhost:5000/interstellar/api/ai_create") @@ -56,6 +64,7 @@ const InputOutputBackend: React.FC = () => { getWorkerRef.current.postMessage({ action: "start", access_token:accessToken}) + addMessage("assistant","") getWorkerRef.current.onmessage = (event) => { const data = event.data @@ -63,7 +72,7 @@ const InputOutputBackend: React.FC = () => { setLiveMessage("error getting AI response: "+ data.error) } else { console.log("Received data:", data); - setLiveMessage(data) + editLastMessage(data.response) } } @@ -75,13 +84,27 @@ const InputOutputBackend: React.FC = () => { const endGetWorker = () => { if (getWorkerRef.current) { - addMessage("assistant", liveMessage) getWorkerRef.current.postMessage({action:"terminate"}) getWorkerRef.current.terminate() console.log(messages); } } + const editLastMessage = (newContent: string) => { + setMessages((prevMessages) => { + const updatedMessages = prevMessages.slice(); // Create a shallow copy of the current messages + if (updatedMessages.length > 0) { + const lastMessage = updatedMessages[updatedMessages.length - 1]; + updatedMessages[updatedMessages.length - 1] = { + ...lastMessage, // Keep the existing role and other properties + content: newContent, // Update only the content + }; + } + return updatedMessages; // Return the updated array + }); +}; + + const addMessage = (role: string, content: string) => { setMessages(previous => [...previous,{role,content}]) } @@ -89,7 +112,7 @@ const InputOutputBackend: React.FC = () => { if (postWorkerRef.current) { addMessage("user", inputValue) console.log("input:",inputValue); - postWorkerRef.current.postMessage({messages:messages, ai_model:"phi3.5", access_token:accessToken}) + postWorkerRef.current.postMessage({messages:[...messages, { role: "user", content: inputValue }], ai_model:"phi3.5", access_token:accessToken}) startGetWorker() } } diff --git a/app/backend/threads/GetWorker.js b/app/backend/threads/GetWorker.js index a89e02b..23d8e63 100644 --- a/app/backend/threads/GetWorker.js +++ b/app/backend/threads/GetWorker.js @@ -1,23 +1,23 @@ import axios from "axios"; -let shouldRun = false +let accesstoken onmessage = (event) => { const { action, access_token } = event.data + accesstoken=access_token + if (action === "start") { - shouldRun = true - fetchData(access_token) + fetchData() } else if (action === "terminate") { - shouldRun = false } } console.log('starting get loop'); -const fetchData = (access_token) => { - if (!shouldRun) return - +const fetchData = () => { + console.log(accesstoken); - const apiURL = "http://localhost:5000/interstellar/api/ai_get?access_token="+access_token + + const apiURL = "http://localhost:5000/interstellar/api/ai_get?access_token="+accesstoken axios.get(apiURL) .then(response => { diff --git a/app/backend/threads/PostWorker.js b/app/backend/threads/PostWorker.js index 5d6ea63..730c852 100644 --- a/app/backend/threads/PostWorker.js +++ b/app/backend/threads/PostWorker.js @@ -2,17 +2,18 @@ import axios from "axios"; onmessage = (e) => { const { messages = [{ role: "assistant", content: "Hello! How can I help you?" }], ai_model = "phi3.5", access_token } = e.data - - const promptedMessage = messages.unshift({role:"system", content:"You are a Helpful assistant"}) + messages.unshift({ role: "system", content: "You are a Helpful assistant" }) const Message = { - messages: promptedMessage, + messages: messages, ai_model: "phi3.5", model_type:"local", access_token:access_token } + console.log(Message); + axios.post("http://localhost:5000/interstellar/api/ai_send",Message) .then(response => { const status = response.data.status From 5a7961b10f6a4c8fb1bb885e3287feb313ee0e90 Mon Sep 17 00:00:00 2001 From: Patrick_Pluto Date: Mon, 23 Sep 2024 16:35:16 +0200 Subject: [PATCH 7/7] WebView better, i think? --- py/api.py | 5 ----- py/requirements.txt | 3 --- py/run.py | 7 +++++++ py/webapp.py | 4 +--- 4 files changed, 8 insertions(+), 11 deletions(-) create mode 100644 py/run.py diff --git a/py/api.py b/py/api.py index 55d0483..be44561 100644 --- a/py/api.py +++ b/py/api.py @@ -118,8 +118,3 @@ class API: ssl_context = ("cert.pem", "key.pem") self.app.run(debug=True, host='0.0.0.0', port=5000) - - -if __name__ == '__main__': - api = API() - api.run() diff --git a/py/requirements.txt b/py/requirements.txt index a5fe3e6..8a79dc1 100644 --- a/py/requirements.txt +++ b/py/requirements.txt @@ -5,7 +5,4 @@ mistralai openai anthropic pyOpenSSL -pywebview -tkhtmlview -cefpython3 pywebview \ No newline at end of file diff --git a/py/run.py b/py/run.py new file mode 100644 index 0000000..612d72e --- /dev/null +++ b/py/run.py @@ -0,0 +1,7 @@ +import os +import webview + +os.system("python api.py") +webview.create_window('Hello world', 'http://localhost:3000') +webview.start() + diff --git a/py/webapp.py b/py/webapp.py index e97750b..8b13789 100644 --- a/py/webapp.py +++ b/py/webapp.py @@ -1,3 +1 @@ -import webview -webview.create_window('Hello world', 'http://localhost:3000') -webview.start() +