From 4b1e3fd5f6fab83eed0b6035c16e9a86e326e617 Mon Sep 17 00:00:00 2001 From: jabrock Date: Mon, 1 Jul 2024 08:18:52 -0700 Subject: [PATCH 1/3] adding settings and code for changing backend service --- app/package.json | 9 +- app/path_mapping.json | 11 ++ app/src/components/content/index.tsx | 148 ++++++++---------- app/src/components/content/settings.tsx | 36 ++++- .../components/content/stomp-interface.tsx | 64 ++++++++ .../content/websocket-interface.tsx | 80 ++++++++++ app/tsconfig.json | 3 +- 7 files changed, 255 insertions(+), 96 deletions(-) create mode 100644 app/src/components/content/stomp-interface.tsx create mode 100644 app/src/components/content/websocket-interface.tsx diff --git a/app/package.json b/app/package.json index 2c1b7c27..f417532b 100644 --- a/app/package.json +++ b/app/package.json @@ -3,14 +3,15 @@ "version": "1.0.0", "description": "Sample Client app showing communication with OCI Generative AI services via Websocket", "dependencies": { - "@oracle/oraclejet": "~16.0.0", - "@oracle/oraclejet-core-pack": "~16.0.0", + "@oracle/oraclejet": "~16.1.0", + "@oracle/oraclejet-core-pack": "~16.1.0", + "@stomp/stompjs": "^7.0.0", "marked": "^4.3.0", "uuid": "^9.0.1" }, "devDependencies": { - "@oracle/ojet-cli": "~16.0.0", - "@oracle/oraclejet-audit": "^16.0.0", + "@oracle/ojet-cli": "~16.1.0", + "@oracle/oraclejet-audit": "^16.1.0", "@types/uuid": "^9.0.7", "extract-zip": "^1.7.0", "fs-extra": "^8.1.0", diff --git a/app/path_mapping.json b/app/path_mapping.json index 128fddc3..609e43a6 100644 --- a/app/path_mapping.json +++ b/app/path_mapping.json @@ -380,6 +380,17 @@ "path": "libs/chai/chai.js", "cdnPath": "chai/chai-4.3.10.min" } + }, + "stompjs": { + "cwd": "node_modules/@stomp/stompjs/bundles", + "debug": { + "src": "stomp.umd.js", + "path": "libs/stompjs/stomp.umd.js" + }, + "release": { + "src": "stomp.umd.min.js", + "path": "libs/stompjs/stomp.umd.min.js" + } } } } diff --git a/app/src/components/content/index.tsx b/app/src/components/content/index.tsx index 64814312..24f8febf 100644 --- a/app/src/components/content/index.tsx +++ b/app/src/components/content/index.tsx @@ -12,8 +12,12 @@ import { InputSearchElement } from "ojs/ojinputsearch"; import { useState, useEffect, useRef } from "preact/hooks"; import * as Questions from "text!./data/questions.json"; import * as Answers from "text!./data/answers.json"; +import { initWebSocket } from "./websocket-interface"; +import { InitStomp, sendPrompt } from "./stomp-interface"; +import { Client } from "@stomp/stompjs"; type ServiceTypes = "text" | "summary" | "sim"; +type BackendTypes = "java" | "python"; type Chat = { id?: number; question?: string; @@ -25,12 +29,13 @@ const Content = () => { const [busy, setBusy] = useState(false); const [summaryResults, setSummaryResults] = useState(""); const [summaryPrompt, setSummaryPrompt] = useState(); - const [serviceType, setServiceType] = useState("summary"); + const [serviceType, setServiceType] = useState("text"); + const [backendType, setBackendType] = useState("java"); const [settingsOpened, setSettingsOpened] = useState(false); const question = useRef(); const chatData = useRef>([]); const socket = useRef(); - const [connState, setConnState] = useState("Disconnected"); + const [client, setClient] = useState(null); const messagesDP = useRef( new MutableArrayDataProvider( @@ -39,77 +44,6 @@ const Content = () => { ) ); - const gateway = `ws://${window.location.hostname}:1986`; - let sockTimer: any = null; - - // setup the websocket connection - const initWebSocket = () => { - console.log("Trying to open a WebSocket connection..."); - socket.current = new WebSocket(gateway); - socket.current.binaryType = "arraybuffer"; - socket.current.onopen = onOpen; - socket.current.onerror = onError; - socket.current.onclose = onClose; - socket.current.onmessage = onMessage; - }; - - // handle all messages coming from the websocket service - const onMessage = (event: any) => { - const msg = JSON.parse(event.data); - - switch (msg.msgType) { - // switch (Object.keys(msg)[0]) { - case "message": - console.log("message: ", msg.data); - return msg.data; - case "question": - console.log("question: ", msg.data); - return msg.data; - case "summary": - console.log("summary"); - setSummaryResults(msg.data); - return; - case "answer": - console.log("answer: ", msg.data); - if (msg.data !== "connected") { - let tempArray = [...chatData.current]; - // remove the animation item before adding answer - setBusy(false); - tempArray.pop(); - messagesDP.current.data = []; - tempArray.push({ - id: tempArray.length as number, - answer: msg.data, - }); - chatData.current = tempArray; - setUpdate(chatData.current); - } - return msg.data; - default: - return "unknown"; - } - }; - - const onOpen = () => { - clearInterval(sockTimer); - console.log("Connection opened"); - socket.current?.send( - JSON.stringify({ msgType: "message", data: "connected" }) - ); - setConnState("Connected"); - }; - - // if the connection is lost, wait one minute and try again. - const onError = () => { - sockTimer = setInterval(initWebSocket, 1000 * 60); - }; - function onClose() { - console.log("Connection closed"); - setConnState("Disconnected"); - socket.current ? (socket.current.onclose = () => {}) : null; - socket.current?.close(); - } - // Simulation code const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); const runSimulation = async () => { @@ -138,7 +72,18 @@ const Content = () => { useEffect(() => { switch (serviceType) { case "text": - initWebSocket(); + if (backendType === "python") { + initWebSocket( + setSummaryResults, + setBusy, + setUpdate, + messagesDP, + socket, + chatData + ); + } else { + setClient(InitStomp(setBusy, setUpdate, messagesDP, chatData)); + } console.log("Running Gen AI"); return; case "sim": @@ -146,13 +91,14 @@ const Content = () => { console.log("running simulation"); return; case "summary": - initWebSocket(); + // initWebSocket(); console.log("summary loading"); return; } return () => { socket.current ? (socket.current.onclose = () => {}) : null; socket.current?.close(); + client?.deactivate(); }; }, [serviceType]); @@ -193,9 +139,13 @@ const Content = () => { // simulating the delay for now just to show what the animation looks like. setTimeout(() => { - socket.current?.send( - JSON.stringify({ msgType: "question", data: question.current }) - ); + if (backendType === "python") { + socket.current?.send( + JSON.stringify({ msgType: "question", data: question.current }) + ); + } else { + sendPrompt(client, question.current!); + } }, 300); } }; @@ -218,7 +168,28 @@ const Content = () => { setUpdate([]); chatData.current = []; setServiceType(service); - toggleDrawer(); + //toggleDrawer(); + }; + const backendTypeChangeHandler = (backend: BackendTypes) => { + setUpdate([]); + chatData.current = []; + setBackendType(backend); + switch (backend) { + case "java": + setClient(InitStomp(setBusy, setUpdate, messagesDP, chatData)); + return; + case "python": + initWebSocket( + setSummaryPrompt, + setBusy, + setUpdate, + messagesDP, + socket, + chatData + ); + return; + } + //toggleDrawer(); }; const clearSummary = () => { @@ -238,8 +209,10 @@ const Content = () => { aria-label="Settings Drawer" >
@@ -257,11 +230,14 @@ const Content = () => {
{serviceType === "text" && ( - + <> + {/* Send Prompt */} + + )} {serviceType === "sim" && ( void; + aiServiceType: ServiceTypeVal; + backendType: BackendTypeVal; + aiServiceChange: (service: ServiceTypeVal) => void; + backendChange: (backend: BackendTypeVal) => void; }; const serviceTypes = [ @@ -20,15 +23,28 @@ const serviceTypes = [ { value: "summary", label: "Summarize" }, { value: "sim", label: "Simulation" }, ]; + +const backendTypes = [ + { value: "java", label: "Java" }, + { value: "python", label: "Python" }, +]; const serviceOptionsDP = new MutableArrayDataProvider< Services["value"], Services >(serviceTypes, { keyAttributes: "value" }); +const backendOptionsDP = new MutableArrayDataProvider< + Services["value"], + Services +>(backendTypes, { keyAttributes: "value" }); export const Settings = (props: Props) => { const handleServiceTypeChange = (event: any) => { if (event.detail.updatedFrom === "internal") - props.serviceChange(event.detail.value); + props.aiServiceChange(event.detail.value); + }; + const handleBackendTypeChange = (event: any) => { + if (event.detail.updatedFrom === "internal") + props.backendChange(event.detail.value); }; return ( @@ -36,13 +52,23 @@ export const Settings = (props: Props) => {

Service Settings

+

Backend Service Type

+ + + ); }; diff --git a/app/src/components/content/stomp-interface.tsx b/app/src/components/content/stomp-interface.tsx new file mode 100644 index 00000000..3604ba48 --- /dev/null +++ b/app/src/components/content/stomp-interface.tsx @@ -0,0 +1,64 @@ +import "preact"; +import { Client } from "stompjs"; + +// setup the Stompjs connection +export const InitStomp = ( + setBusy: any, + setUpdate: any, + messagesDP: any, + chatData: any +) => { + //const [test, setTest] = useState(); + const client = new Client({ + brokerURL: "ws://localhost:8080/websocket", + onConnect: () => { + client.subscribe("/user/queue/answer", (message: any) => { + console.log("Answer message: ", JSON.parse(message.body).content); + onMessage(message); + }); + }, + onStompError: (e) => { + console.log("Stomp Error: ", e); + }, + onWebSocketError: () => { + console.log("Error connecting to Websocket service"); + client.deactivate(); + }, + }); + client.activate(); + + const onMessage = (msg: any) => { + let aiAnswer = JSON.parse(msg.body).content; + // console.log("answer: ", aiAnswer); + if (msg.data !== "connected") { + let tempArray = [...chatData.current]; + // remove the animation item before adding answer + setBusy(false); + tempArray.pop(); + messagesDP.current.data = []; + tempArray.push({ + id: tempArray.length as number, + answer: aiAnswer, + }); + chatData.current = tempArray; + setUpdate(chatData.current); + } + }; + return client; +}; + +export const sendPrompt = (client: Client | null, prompt: string) => { + if (client?.connected) { + console.log("Sending prompt: ", prompt); + client.publish({ + destination: "/genai/prompt", + body: JSON.stringify({ + conversationId: "something", + content: prompt, //"Show me code for a websocket service using JavaScript", + modelId: "notapply", + }), + }); + } else { + console.log("Error, no Stomp connection"); + } +}; diff --git a/app/src/components/content/websocket-interface.tsx b/app/src/components/content/websocket-interface.tsx new file mode 100644 index 00000000..013f5064 --- /dev/null +++ b/app/src/components/content/websocket-interface.tsx @@ -0,0 +1,80 @@ +import "preact"; + +const gateway = `ws://${window.location.hostname}:1986`; +let sockTimer: any = null; + +export const initWebSocket = ( + setSummaryResults: any, + setBusy: any, + setUpdate: any, + messagesDP: any, + socket: any, + chatData: any +) => { + // const [connState, setConnState] = useState("Disconnected"); + console.log("Trying to open a WebSocket connection..."); + socket.current = new WebSocket(gateway); + socket.current.binaryType = "arraybuffer"; + + // handle all messages coming from the websocket service + const onMessage = (event: any) => { + const msg = JSON.parse(event.data); + + switch (msg.msgType) { + // switch (Object.keys(msg)[0]) { + case "message": + console.log("message: ", msg.data); + return msg.data; + case "question": + console.log("question: ", msg.data); + return msg.data; + case "summary": + console.log("summary"); + setSummaryResults(msg.data); + return; + case "answer": + console.log("answer: ", msg.data); + if (msg.data !== "connected") { + let tempArray = [...chatData.current]; + // remove the animation item before adding answer + setBusy(false); + tempArray.pop(); + messagesDP.current.data = []; + tempArray.push({ + id: tempArray.length as number, + answer: msg.data, + }); + chatData.current = tempArray; + setUpdate(chatData.current); + } + return msg.data; + default: + return "unknown"; + } + }; + + const onOpen = () => { + clearInterval(sockTimer); + console.log("Connection opened"); + socket.current?.send( + JSON.stringify({ msgType: "message", data: "connected" }) + ); + //setConnState("Connected"); + }; + + // if the connection is lost, wait one minute and try again. + const onError = () => { + //sockTimer = setInterval(initWebSocket, 1000 * 60); + }; + function onClose() { + console.log("Connection closed"); + //setConnState("Disconnected"); + socket.current ? (socket.current.onclose = () => {}) : null; + socket.current?.close(); + } + + socket.current.onopen = onOpen; + socket.current.onerror = onError; + socket.current.onclose = onClose; + socket.current.onmessage = onMessage; +}; diff --git a/app/tsconfig.json b/app/tsconfig.json index 7afa2973..76b073dc 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -22,7 +22,8 @@ "preact": ["./node_modules/preact"], "marked/*": ["./node_modules/marked/*"], "oj-sample/*": ["./src/components/oj-sample/types/*"], - "md-wrapper": ["./src/components/md-wrapper/types/index"] + "md-wrapper": ["./src/components/md-wrapper/types/index"], + "stompjs": ["./node_modules/@stomp/stompjs/index"] }, "declaration": true, "noEmitOnError": true, From bb8237cb24784de2dfe98cbd4e21f0a76cc3166f Mon Sep 17 00:00:00 2001 From: jabrock Date: Sun, 21 Jul 2024 16:19:47 -0700 Subject: [PATCH 2/3] updating services for Java backend --- app/package.json | 2 + app/scripts/hooks/before_serve.js | 23 +++-- app/src/components/content/index.tsx | 81 ++++++++-------- app/src/components/content/settings.tsx | 46 ++++++++- .../components/content/stomp-interface.tsx | 32 +++++-- app/src/components/content/summary.tsx | 94 +++++++++++++++---- .../content/websocket-interface.tsx | 2 +- 7 files changed, 204 insertions(+), 76 deletions(-) diff --git a/app/package.json b/app/package.json index f417532b..53c11b5a 100644 --- a/app/package.json +++ b/app/package.json @@ -13,11 +13,13 @@ "@oracle/ojet-cli": "~16.1.0", "@oracle/oraclejet-audit": "^16.1.0", "@types/uuid": "^9.0.7", + "express-http-proxy": "^2.0.0", "extract-zip": "^1.7.0", "fs-extra": "^8.1.0", "glob": "7.2.0", "typescript": "5.3.2", "underscore": "^1.10.2", + "url": "^0.11.3", "yargs-parser": "13.1.2" }, "engines": { diff --git a/app/scripts/hooks/before_serve.js b/app/scripts/hooks/before_serve.js index 40a9ea84..ba495a0e 100644 --- a/app/scripts/hooks/before_serve.js +++ b/app/scripts/hooks/before_serve.js @@ -5,19 +5,28 @@ */ -'use strict'; +"use strict"; module.exports = function (configObj) { return new Promise((resolve, reject) => { - console.log('Running before_serve hook.'); + console.log("Running before_serve hook."); // ojet custom connect and serve options - // { connectOpts, serveOpts } = configObj; - // const express = require('express'); - // const http = require('http'); + const { connectOpts, serveOpts } = configObj; + const express = require("express"); + const http = require("http"); + const proxy = require("express-http-proxy"); + const url = require("url"); + + // New hostname+path as specified by question: + const apiProxy = proxy("http://localhost:8080", { + proxyReqPathResolver: (req) => url.parse("/api" + req.url).path, + }); + const app = express(); + app.use("/api", apiProxy); // pass back custom http - // configObj['http'] = http; + configObj["http"] = http; // pass back custom express app - // configObj['express'] = express(); + configObj["express"] = app; // pass back custom options for http.createServer // const serverOptions = {...}; // configObj['serverOptions'] = serverOptions; diff --git a/app/src/components/content/index.tsx b/app/src/components/content/index.tsx index 24f8febf..465712e7 100644 --- a/app/src/components/content/index.tsx +++ b/app/src/components/content/index.tsx @@ -24,13 +24,21 @@ type Chat = { answer?: string; loading?: string; }; + +const defaultServiceType: string = localStorage.getItem("service") || "text"; +const defaultBackendType: String = localStorage.getItem("backend") || "java"; + const Content = () => { const [update, setUpdate] = useState>([]); const [busy, setBusy] = useState(false); - const [summaryResults, setSummaryResults] = useState(""); + const [summaryResults, setSummaryResults] = useState(""); const [summaryPrompt, setSummaryPrompt] = useState(); - const [serviceType, setServiceType] = useState("text"); - const [backendType, setBackendType] = useState("java"); + const [serviceType, setServiceType] = useState( + defaultServiceType as ServiceTypes + ); + const [backendType, setBackendType] = useState( + defaultBackendType as BackendTypes + ); const [settingsOpened, setSettingsOpened] = useState(false); const question = useRef(); const chatData = useRef>([]); @@ -55,7 +63,6 @@ const Content = () => { if (Q) { if (x > 0) tempArray.pop(); tempArray.push({ question: JSON.parse(Questions)[x] }); - // tempArray.push({ loading: "loading" }); Q = false; x++; } else { @@ -82,17 +89,32 @@ const Content = () => { chatData ); } else { - setClient(InitStomp(setBusy, setUpdate, messagesDP, chatData)); + setClient( + InitStomp(setBusy, setUpdate, messagesDP, chatData, serviceType) + ); } - console.log("Running Gen AI"); + console.log("Running Generative service"); return; case "sim": runSimulation(); - console.log("running simulation"); + console.log("Running simulation"); return; case "summary": - // initWebSocket(); - console.log("summary loading"); + if (backendType === "python") { + initWebSocket( + setSummaryResults, + setBusy, + setUpdate, + messagesDP, + socket, + chatData + ); + } else { + setClient( + InitStomp(setBusy, setUpdate, messagesDP, chatData, serviceType) + ); + } + console.log("Running Summarization service"); return; } return () => { @@ -114,7 +136,6 @@ const Content = () => { autoTimeout: "on", }, ]; - //alert("Still waiting for an answer! Hang in there a little longer."); return; } if (event.detail.value) { @@ -165,31 +186,17 @@ const Content = () => { }; const serviceTypeChangeHandler = (service: ServiceTypes) => { + localStorage.setItem("service", service); setUpdate([]); chatData.current = []; setServiceType(service); - //toggleDrawer(); }; const backendTypeChangeHandler = (backend: BackendTypes) => { setUpdate([]); chatData.current = []; setBackendType(backend); - switch (backend) { - case "java": - setClient(InitStomp(setBusy, setUpdate, messagesDP, chatData)); - return; - case "python": - initWebSocket( - setSummaryPrompt, - setBusy, - setUpdate, - messagesDP, - socket, - chatData - ); - return; - } - //toggleDrawer(); + localStorage.setItem("backend", backend); + location.reload(); }; const clearSummary = () => { @@ -199,6 +206,9 @@ const Content = () => { const updateSummaryPrompt = (val: string) => { setSummaryPrompt(val); }; + const updateSummaryResults = (summary: string) => { + setSummaryResults(summary); + }; return (
@@ -221,23 +231,18 @@ const Content = () => { position="top" onojClose={handleToastClose} > - {/*

*/}
- {/*
{connState}
*/}
{serviceType === "text" && ( - <> - {/* Send Prompt */} - - + )} {serviceType === "sim" && ( { {serviceType === "summary" && ( )} diff --git a/app/src/components/content/settings.tsx b/app/src/components/content/settings.tsx index c73f3c6e..70004977 100644 --- a/app/src/components/content/settings.tsx +++ b/app/src/components/content/settings.tsx @@ -1,7 +1,8 @@ import "preact"; -import { useState } from "preact/hooks"; +import { useEffect, useRef } from "preact/hooks"; import "oj-c/radioset"; import "oj-c/form-layout"; +import "oj-c/select-single"; import { CRadiosetElement } from "oj-c/radioset"; import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); @@ -47,28 +48,63 @@ export const Settings = (props: Props) => { props.backendChange(event.detail.value); }; + const modelDP = useRef( + new MutableArrayDataProvider([], { keyAttributes: "id" }) + ); + + const fetchModels = async () => { + try { + const response = await fetch("/api/genai/models"); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + const json = await response.json(); + modelDP.current.data = json; + } catch (error: any) { + console.log( + "Java service not available for fetching list of Models: ", + error.message + ); + } + }; + + useEffect(() => { + fetchModels(); + }, []); return (
-

Service Settings

+

AI service types

-

Backend Service Type

+

Backend service types

+ {props.aiServiceType == "summary" && props.backendType == "java" && ( + <> +

Model options

+ + + + + )}
); }; diff --git a/app/src/components/content/stomp-interface.tsx b/app/src/components/content/stomp-interface.tsx index 3604ba48..5932a7e0 100644 --- a/app/src/components/content/stomp-interface.tsx +++ b/app/src/components/content/stomp-interface.tsx @@ -6,22 +6,40 @@ export const InitStomp = ( setBusy: any, setUpdate: any, messagesDP: any, - chatData: any + chatData: any, + serviceType: any ) => { //const [test, setTest] = useState(); + const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"; + const hostname = + window.location.hostname === "localhost" + ? "localhost:8080" + : window.location.hostname; + const serviceURL = `${protocol}${hostname}/websocket`; + console.log("in the stomp init module"); const client = new Client({ - brokerURL: "ws://localhost:8080/websocket", + brokerURL: serviceURL, onConnect: () => { - client.subscribe("/user/queue/answer", (message: any) => { - console.log("Answer message: ", JSON.parse(message.body).content); - onMessage(message); - }); + if (serviceType === "text") { + client.subscribe("/user/queue/answer", (message: any) => { + console.log("Answer message: ", JSON.parse(message.body).content); + onMessage(message); + }); + } else if (serviceType === "summary") { + client.subscribe("/user/queue/summary", (message: any) => { + console.log("Summary message: ", JSON.parse(message.body).content); + onMessage(message); + }); + } }, onStompError: (e) => { console.log("Stomp Error: ", e); }, onWebSocketError: () => { console.log("Error connecting to Websocket service"); + serviceType === "text" + ? client.unsubscribe("/user/queue/answer") + : client.unsubscribe("/user/queue/summary"); client.deactivate(); }, }); @@ -29,7 +47,7 @@ export const InitStomp = ( const onMessage = (msg: any) => { let aiAnswer = JSON.parse(msg.body).content; - // console.log("answer: ", aiAnswer); + //console.log("answer: ", aiAnswer); if (msg.data !== "connected") { let tempArray = [...chatData.current]; // remove the animation item before adding answer diff --git a/app/src/components/content/summary.tsx b/app/src/components/content/summary.tsx index 275c50a4..774029df 100644 --- a/app/src/components/content/summary.tsx +++ b/app/src/components/content/summary.tsx @@ -23,18 +23,35 @@ declare global { } type Props = { fileChanged: (file: ArrayBuffer) => void; - summary: string | null; clear: () => void; prompt: (val: string) => void; + summaryChanged: (summary: string) => void; + summary: string; + backendType: any; }; - +const protocol = window.location.protocol === "http:" ? "ws://" : "wss://"; +const hostname = + window.location.hostname === "localhost" + ? "localhost:8080" + : window.location.hostname; +const serviceRootURL = `${protocol}${hostname}`; const acceptArr: string[] = ["application/pdf", "*.pdf"]; const messages: { id: number; severity: string; summary: string }[] = []; +const FILE_SIZE = 120000; -export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { +export const Summary = ({ + fileChanged, + clear, + prompt, + summaryChanged, + summary, + backendType, +}: Props) => { const [invalidMessage, setInvalidMessage] = useState(null); const [summaryPrompt, setSummaryPrompt] = useState(""); + const [summaryResults, setSummaryResults] = useState(summary); const [fileNames, setFileNames] = useState(null); + const [file, setFile] = useState(null); const [messages, setMessages] = useState([]); const [pdfFile, setPDFFile] = useState(); const [loading, setLoading] = useState(false); @@ -62,6 +79,29 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { prompt(tempStr); }; + const sendToJavaBackend = async (file: File, prompt: string) => { + const formData = new FormData(); + formData.append("file", file); + + const res = await fetch("/api/upload", { + // const res = await fetch("http://localhost:5173/api/upload", { + method: "POST", + mode: "cors", + referrerPolicy: "strict-origin-when-cross-origin", + body: formData, + }); + console.log("Response: ", res); + const responseData = await res.json(); + const { content, errorMessage } = responseData; + if (errorMessage.length) { + // setErrorMessage(errorMessage); + // setShowError(true); + } else { + console.log("Response: ", content); + setSummaryResults(content); + summaryChanged(content); + } + }; // FilePicker related methods const selectListener = async (event: CFilePickerElement.ojSelect) => { setInvalidMessage(""); @@ -71,14 +111,19 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { return file?.name; }); - const fr = new FileReader(); - let ab = new ArrayBuffer(200000000); - fr.onload = (ev: ProgressEvent) => { - let ab = fr.result; - setPDFFile(ab as ArrayBuffer); - }; - fr.readAsArrayBuffer(files[0]); - setFileNames(names); + if (backendType === "java") { + setFile(files[0]); + setFileNames(names); + } else { + const fr = new FileReader(); + let ab = new ArrayBuffer(200000000); + fr.onload = (ev: ProgressEvent) => { + let ab = fr.result; + setPDFFile(ab as ArrayBuffer); + }; + fr.readAsArrayBuffer(files[0]); + setFileNames(names); + } }; const buildSummaryData = (rawData: ArrayBuffer) => { @@ -123,8 +168,9 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { for (let i = 0; i < files.length; i++) { file = files[i]; - // Cohere has a character limit of 100kb so we are restricting it here as well. - if (file.size > 100000) { + // Cohere has a character limit of ~100kb so we are restricting it here as well. + // We can use LangChain in this area to support larger files. + if (file.size > FILE_SIZE) { tempArray.push(file.name); invalidFiles.current = tempArray; } @@ -141,7 +187,7 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { summary: "File " + invalidFiles.current[0] + - " is too big. The maximum size is 100KB.", + ` is too big. The maximum size is ${FILE_SIZE / 1000}KB.`, }); setMessages(temp); } else { @@ -153,7 +199,7 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { summary: "These files are too big: " + fileNames + - ". The maximum size is 100KB.", + `. The maximum size is ${FILE_SIZE / 1000}KB.`, }); setMessages(temp); } @@ -163,7 +209,11 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { }; useEffect(() => { - if (summary !== "") setLoading(!loading); + if (summaryResults !== "") setLoading(!loading); + }, [summaryResults]); + + useEffect(() => { + setSummaryResults(summary); }, [summary]); useEffect(() => { @@ -193,7 +243,11 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { console.log("Calling websocket API to process PDF"); console.log("Filename: ", fileNames); console.log("Prompt: ", summaryPrompt); - fileChanged(buildSummaryData(pdfFile as ArrayBuffer)); + if (backendType === "python") { + fileChanged(buildSummaryData(pdfFile as ArrayBuffer)); + } else { + sendToJavaBackend(file!, summaryPrompt); + } setLoading(true); } }; @@ -224,7 +278,9 @@ export const Summary = ({ fileChanged, summary, clear, prompt }: Props) => { onojSelect={selectListener} onojInvalidSelect={invalidListener} onojBeforeSelect={beforeSelectListener} - secondaryText="Maximum file size is 100KB per PDF file." + secondaryText={`Maximum file size is ${ + FILE_SIZE / 1000 + }KB per PDF file.`} > { )} diff --git a/app/src/components/content/websocket-interface.tsx b/app/src/components/content/websocket-interface.tsx index 013f5064..2edc5ee5 100644 --- a/app/src/components/content/websocket-interface.tsx +++ b/app/src/components/content/websocket-interface.tsx @@ -29,7 +29,7 @@ export const initWebSocket = ( console.log("question: ", msg.data); return msg.data; case "summary": - console.log("summary"); + console.log("summary: ", msg.data); setSummaryResults(msg.data); return; case "answer": From 4e4f23dd83b5fe960ac17f1d795faffe8f6e8c37 Mon Sep 17 00:00:00 2001 From: jabrock Date: Tue, 13 Aug 2024 17:34:24 -0700 Subject: [PATCH 3/3] updating conversationId and other cleanup --- app/src/components/app.tsx | 11 +++++-- app/src/components/content/index.tsx | 31 +++++++++++-------- app/src/components/content/settings.tsx | 8 +++-- .../components/content/stomp-interface.tsx | 13 +++++--- app/src/components/content/summary.tsx | 22 +++++++------ 5 files changed, 52 insertions(+), 33 deletions(-) diff --git a/app/src/components/app.tsx b/app/src/components/app.tsx index cf8f46e5..1a9ffa6c 100644 --- a/app/src/components/app.tsx +++ b/app/src/components/app.tsx @@ -1,19 +1,24 @@ import { Header } from "./header"; import Content from "./content/index"; import { registerCustomElement } from "ojs/ojvcomponent"; -import "preact"; +import { createContext } from "preact"; type Props = { appName: string; }; +const convoUUID = window.crypto.randomUUID(); +export const ConvoCtx = createContext(convoUUID); export const App = registerCustomElement("app-root", (props: Props) => { props.appName = "Generative AI JET UI"; return (
-
- + + {console.log("UUID: ", convoUUID)} +
+ +
); }); diff --git a/app/src/components/content/index.tsx b/app/src/components/content/index.tsx index 465712e7..84df4f33 100644 --- a/app/src/components/content/index.tsx +++ b/app/src/components/content/index.tsx @@ -9,12 +9,13 @@ import "oj-c/drawer-popup"; import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); import { MessageToastItem } from "oj-c/message-toast"; import { InputSearchElement } from "ojs/ojinputsearch"; -import { useState, useEffect, useRef } from "preact/hooks"; +import { useState, useEffect, useRef, useContext } from "preact/hooks"; import * as Questions from "text!./data/questions.json"; import * as Answers from "text!./data/answers.json"; import { initWebSocket } from "./websocket-interface"; import { InitStomp, sendPrompt } from "./stomp-interface"; import { Client } from "@stomp/stompjs"; +import { ConvoCtx } from "../app"; type ServiceTypes = "text" | "summary" | "sim"; type BackendTypes = "java" | "python"; @@ -26,12 +27,14 @@ type Chat = { }; const defaultServiceType: string = localStorage.getItem("service") || "text"; -const defaultBackendType: String = localStorage.getItem("backend") || "java"; +const defaultBackendType: string = localStorage.getItem("backend") || "java"; const Content = () => { + const conversationId = useContext(ConvoCtx); const [update, setUpdate] = useState>([]); const [busy, setBusy] = useState(false); const [summaryResults, setSummaryResults] = useState(""); + const [modelId, setModelId] = useState(null); const [summaryPrompt, setSummaryPrompt] = useState(); const [serviceType, setServiceType] = useState( defaultServiceType as ServiceTypes @@ -76,6 +79,7 @@ const Content = () => { await sleep(2000); } }; + useEffect(() => { switch (serviceType) { case "text": @@ -158,16 +162,13 @@ const Content = () => { setUpdate(chatData.current); setBusy(true); - // simulating the delay for now just to show what the animation looks like. - setTimeout(() => { - if (backendType === "python") { - socket.current?.send( - JSON.stringify({ msgType: "question", data: question.current }) - ); - } else { - sendPrompt(client, question.current!); - } - }, 300); + if (backendType === "python") { + socket.current?.send( + JSON.stringify({ msgType: "question", data: question.current }) + ); + } else { + sendPrompt(client, question.current!, modelId!, conversationId!); + } } }; @@ -198,7 +199,10 @@ const Content = () => { localStorage.setItem("backend", backend); location.reload(); }; - + const modelIdChangeHandler = (event: CustomEvent) => { + console.log("model Id: ", event.detail.value); + if (event.detail.value != null) setModelId(event.detail.value); + }; const clearSummary = () => { setSummaryResults(""); }; @@ -223,6 +227,7 @@ const Content = () => { backendType={backendType} aiServiceChange={serviceTypeChangeHandler} backendChange={backendTypeChangeHandler} + modelIdChange={modelIdChangeHandler} />
diff --git a/app/src/components/content/settings.tsx b/app/src/components/content/settings.tsx index 70004977..3863b301 100644 --- a/app/src/components/content/settings.tsx +++ b/app/src/components/content/settings.tsx @@ -1,4 +1,4 @@ -import "preact"; +import { ComponentProps } from "preact"; import { useEffect, useRef } from "preact/hooks"; import "oj-c/radioset"; import "oj-c/form-layout"; @@ -17,13 +17,14 @@ type Props = { backendType: BackendTypeVal; aiServiceChange: (service: ServiceTypeVal) => void; backendChange: (backend: BackendTypeVal) => void; + modelIdChange: (modelName: any) => void; }; const serviceTypes = [ { value: "text", label: "Generative Text" }, { value: "summary", label: "Summarize" }, - { value: "sim", label: "Simulation" }, ]; +// { value: "sim", label: "Simulation" }, const backendTypes = [ { value: "java", label: "Java" }, @@ -93,7 +94,7 @@ export const Settings = (props: Props) => { onvalueChanged={handleBackendTypeChange} > - {props.aiServiceType == "summary" && props.backendType == "java" && ( + {props.aiServiceType == "text" && props.backendType == "java" && ( <>

Model options

@@ -101,6 +102,7 @@ export const Settings = (props: Props) => { data={modelDP.current} labelHint={"Model"} itemText={"name"} + onvalueChanged={props.modelIdChange} > diff --git a/app/src/components/content/stomp-interface.tsx b/app/src/components/content/stomp-interface.tsx index 5932a7e0..cd77256a 100644 --- a/app/src/components/content/stomp-interface.tsx +++ b/app/src/components/content/stomp-interface.tsx @@ -65,15 +65,20 @@ export const InitStomp = ( return client; }; -export const sendPrompt = (client: Client | null, prompt: string) => { +export const sendPrompt = ( + client: Client | null, + prompt: string, + modelId: string, + convoId: string +) => { if (client?.connected) { console.log("Sending prompt: ", prompt); client.publish({ destination: "/genai/prompt", body: JSON.stringify({ - conversationId: "something", - content: prompt, //"Show me code for a websocket service using JavaScript", - modelId: "notapply", + conversationId: convoId, + content: prompt, + modelId: modelId, }), }); } else { diff --git a/app/src/components/content/summary.tsx b/app/src/components/content/summary.tsx index 774029df..960bd67e 100644 --- a/app/src/components/content/summary.tsx +++ b/app/src/components/content/summary.tsx @@ -282,16 +282,18 @@ export const Summary = ({ FILE_SIZE / 1000 }KB per PDF file.`} > - + {backendType === "python" && ( + + )} {invalidFiles.current.length !== 1 && fileNames && ( <>