Skip to content

Commit

Permalink
Merge pull request #24 from oracle-devrel/backendServiceOptions
Browse files Browse the repository at this point in the history
Backend service options
  • Loading branch information
WSPluta authored Aug 14, 2024
2 parents 3107aa5 + 4e4f23d commit 09536a1
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 145 deletions.
11 changes: 7 additions & 4 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
11 changes: 11 additions & 0 deletions app/path_mapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
23 changes: 16 additions & 7 deletions app/scripts/hooks/before_serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 8 additions & 3 deletions app/src/components/app.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div id="appContainer" class="oj-web-applayout-page">
<Header appName={props.appName} />
<Content />
<ConvoCtx.Provider value={convoUUID}>
{console.log("UUID: ", convoUUID)}
<Header appName={props.appName} />
<Content />
</ConvoCtx.Provider>
</div>
);
});
170 changes: 79 additions & 91 deletions app/src/components/content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Array<object>>([]);
const [busy, setBusy] = useState<boolean>(false);
const [summaryResults, setSummaryResults] = useState<string | null>("");
const [summaryResults, setSummaryResults] = useState<string>("");
const [modelId, setModelId] = useState<string | null>(null);
const [summaryPrompt, setSummaryPrompt] = useState<string>();
const [serviceType, setServiceType] = useState<ServiceTypes>("summary");
const [serviceType, setServiceType] = useState<ServiceTypes>(
defaultServiceType as ServiceTypes
);
const [backendType, setBackendType] = useState<BackendTypes>(
defaultBackendType as BackendTypes
);
const [settingsOpened, setSettingsOpened] = useState<boolean>(false);
const question = useRef<string>();
const chatData = useRef<Array<object>>([]);
const socket = useRef<WebSocket>();
const [connState, setConnState] = useState<string>("Disconnected");
const [client, setClient] = useState<Client | null>(null);

const messagesDP = useRef(
new MutableArrayDataProvider<MessageToastItem["summary"], MessageToastItem>(
Expand All @@ -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 () => {
Expand All @@ -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 {
Expand All @@ -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]);

Expand All @@ -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) {
Expand All @@ -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!);
}
}
};

Expand All @@ -215,19 +187,32 @@ 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("");
};

const updateSummaryPrompt = (val: string) => {
setSummaryPrompt(val);
};
const updateSummaryResults = (summary: string) => {
setSummaryResults(summary);
};

return (
<div class="oj-web-applayout-max-width oj-web-applayout-content oj-flex oj-sm-flex-direction-column demo-bg-main">
Expand All @@ -238,8 +223,11 @@ const Content = () => {
aria-label="Settings Drawer"
>
<Settings
serviceType={serviceType}
serviceChange={serviceTypeChangeHandler}
aiServiceType={serviceType}
backendType={backendType}
aiServiceChange={serviceTypeChangeHandler}
backendChange={backendTypeChangeHandler}
modelIdChange={modelIdChangeHandler}
/>
</oj-c-drawer-popup>
<div class="oj-flex-bar oj-flex-item demo-header oj-sm-12">
Expand All @@ -248,9 +236,7 @@ const Content = () => {
position="top"
onojClose={handleToastClose}
></oj-c-message-toast>
{/* <h1 class="oj-typography-heading-lg oj-flex-bar-start"> </h1> */}
<div class="oj-flex-bar-end oj-color-invert demo-header-end">
{/* <h6 class="oj-sm-margin-2x-end">{connState}</h6> */}
<oj-button onojAction={toggleDrawer} label="Toggle" display="icons">
<span slot="startIcon" class="oj-ux-ico-menu"></span>
</oj-button>
Expand All @@ -273,9 +259,11 @@ const Content = () => {
{serviceType === "summary" && (
<Summary
fileChanged={handleFileUpload}
summaryChanged={updateSummaryResults}
summary={summaryResults}
clear={clearSummary}
prompt={updateSummaryPrompt}
backendType={backendType}
/>
)}
</div>
Expand Down
Loading

0 comments on commit 09536a1

Please sign in to comment.