diff --git a/app/package.json b/app/package.json
index 2c1b7c27..53c11b5a 100644
--- a/app/package.json
+++ b/app/package.json
@@ -3,20 +3,23 @@
"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",
+ "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/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/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/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 64814312..84df4f33 100644
--- a/app/src/components/content/index.tsx
+++ b/app/src/components/content/index.tsx
@@ -9,28 +9,44 @@ 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";
type Chat = {
id?: number;
question?: string;
answer?: string;
loading?: string;
};
+
+const defaultServiceType: string = localStorage.getItem("service") || "text";
+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 [summaryResults, setSummaryResults] = useState("");
+ const [modelId, setModelId] = useState(null);
const [summaryPrompt, setSummaryPrompt] = useState();
- const [serviceType, setServiceType] = useState("summary");
+ 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>([]);
const socket = useRef();
- const [connState, setConnState] = useState("Disconnected");
+ const [client, setClient] = useState(null);
const messagesDP = useRef(
new MutableArrayDataProvider(
@@ -39,77 +55,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 () => {
@@ -121,7 +66,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 {
@@ -135,24 +79,52 @@ const Content = () => {
await sleep(2000);
}
};
+
useEffect(() => {
switch (serviceType) {
case "text":
- initWebSocket();
- console.log("Running Gen AI");
+ if (backendType === "python") {
+ initWebSocket(
+ setSummaryResults,
+ setBusy,
+ setUpdate,
+ messagesDP,
+ socket,
+ chatData
+ );
+ } else {
+ setClient(
+ InitStomp(setBusy, setUpdate, messagesDP, chatData, serviceType)
+ );
+ }
+ 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 () => {
socket.current ? (socket.current.onclose = () => {}) : null;
socket.current?.close();
+ client?.deactivate();
};
}, [serviceType]);
@@ -168,7 +140,6 @@ const Content = () => {
autoTimeout: "on",
},
];
- //alert("Still waiting for an answer! Hang in there a little longer.");
return;
}
if (event.detail.value) {
@@ -191,12 +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 })
);
- }, 300);
+ } else {
+ sendPrompt(client, question.current!, modelId!, conversationId!);
+ }
}
};
@@ -215,12 +187,22 @@ 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);
+ 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("");
};
@@ -228,6 +210,9 @@ const Content = () => {
const updateSummaryPrompt = (val: string) => {
setSummaryPrompt(val);
};
+ const updateSummaryResults = (summary: string) => {
+ setSummaryResults(summary);
+ };
return (
@@ -238,8 +223,11 @@ const Content = () => {
aria-label="Settings Drawer"
>
)}
diff --git a/app/src/components/content/websocket-interface.tsx b/app/src/components/content/websocket-interface.tsx
new file mode 100644
index 00000000..2edc5ee5
--- /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: ", msg.data);
+ 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,