From 3fe49489bda20769fbc0a6e28503d5be66fad960 Mon Sep 17 00:00:00 2001 From: "vachand@microsoft.com" Date: Fri, 21 Jun 2024 14:54:42 +0530 Subject: [PATCH 01/19] no auth login for local dev --- docker-compose.yml | 5 +- server/app/main.py | 10 +- studio/src/constants.ts | 7 ++ studio/src/index.tsx | 19 ++-- studio/src/pages/app.tsx | 166 ++++++++++++++++++++----------- studio/src/pages/editor-page.tsx | 65 +++++++----- studio/src/pages/home-page.tsx | 68 +++++++------ 7 files changed, 220 insertions(+), 120 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9e59d3d..0d7ac6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: build: context: . dockerfile: ./server/Dockerfile - command: uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 3000 + command: uvicorn app.main:app --workers 1 --host 0.0.0.0 --port 3000 restart: unless-stopped ports: - "3000:3000" @@ -31,6 +31,8 @@ services: - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY} - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION} - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT} + - NO_AUTH=${NO_AUTH} + - DEFAULT_AUTH_OID=1 # Add all Azure OpenAI keys depends_on: - postgres @@ -48,6 +50,7 @@ services: - VITE_REACT_APP_SERVER_HOST=${SERVER_HOST} - VITE_REACT_APP_INSIGHTS_KEY=${APPINSIGHTS_INSTRUMENTATIONKEY} - VITE_REACT_EDITOR=code + - VITE_REACT_APP_NO_AUTH=${NO_AUTH} ports: - "4173:4173" depends_on: diff --git a/server/app/main.py b/server/app/main.py index b5ee02f..ebb6b5d 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -80,6 +80,9 @@ def get_db(): AAD_APP_CLIENT_ID = os.environ["AAD_APP_CLIENT_ID"] AAD_APP_TENANT_ID = os.environ["AAD_APP_TENANT_ID"] # ISSUER = os.environ["ISSUER"] +IS_NO_AUTH = os.environ["NO_AUTH"] +DEFAULT_OID = os.environ["DEFAULT_AUTH_OID"] + configReq = requests.get( f"https://login.microsoftonline.com/{AAD_APP_TENANT_ID}/.well-known/openid-configuration" ) @@ -176,7 +179,12 @@ async def add_process_time_header(req: Request, call_next): return JSONResponse(status_code=401, content="") try: token = req.headers["authorization"] - verification_of_token = await verify_jwt(token) + + verification_of_token = None + if IS_NO_AUTH: + verification_of_token = {"oid": str(DEFAULT_OID)} + else: + verification_of_token = await verify_jwt(token) if verification_of_token: user_id = verification_of_token["oid"] diff --git a/studio/src/constants.ts b/studio/src/constants.ts index c5df6ed..1c53dee 100644 --- a/studio/src/constants.ts +++ b/studio/src/constants.ts @@ -1,4 +1,5 @@ const APIHOST = import.meta.env.VITE_REACT_APP_SERVER_HOST; +const NOAUTH = import.meta.env.VITE_REACT_APP_NO_AUTH; const removeProtocol = (url:string) => { return url.replace(/^(https?:\/\/)/, ""); @@ -10,3 +11,9 @@ const host = removeProtocol(APIHOST); export const APIHost = isSecure ? `https://${host}` : `http://${host}`; export const WebSocketHost = isSecure ? `wss://${host}` : `ws://${host}`; +export const NoAuth = NOAUTH ? true : false; + +export const DefaultName = "a"; +export const DefaultOID = "1"; +export const DefaultEmail = "a@b.com"; +export const DefaultAuthToken = "1234"; diff --git a/studio/src/index.tsx b/studio/src/index.tsx index 784f93b..8bf32f3 100644 --- a/studio/src/index.tsx +++ b/studio/src/index.tsx @@ -13,6 +13,7 @@ import { App, HomePage, EditorPage } from './pages'; import { AppInsightsContext } from '@microsoft/applicationinsights-react-js'; import { reactPlugin } from './applicationInsightsService'; import { msalConfig } from "./authConfig"; +import { NoAuth } from './constants'; import './i18n'; const msalInstance = new PublicClientApplication(msalConfig); @@ -32,12 +33,18 @@ const authenticatedRouter = createHashRouter([ } ]); -const unAuthenticatedRouter = createHashRouter([ - { - path: "/", - element: , - } -]); +let unAuthenticatedRouter = null; +if (NoAuth) { + unAuthenticatedRouter = authenticatedRouter; +} +else { + unAuthenticatedRouter = createHashRouter([ + { + path: "/", + element: , + } + ]); +} // Inject some global styles mergeStyles({ diff --git a/studio/src/pages/app.tsx b/studio/src/pages/app.tsx index ebdd36e..bf0d5c9 100644 --- a/studio/src/pages/app.tsx +++ b/studio/src/pages/app.tsx @@ -4,7 +4,7 @@ import '../styles/App.css'; import { AuthenticatedTemplate, UnauthenticatedTemplate, useMsal } from "@azure/msal-react"; import { EventType } from "@azure/msal-browser"; import { sendRequest } from './../api'; -import { APIHost } from './../constants'; +import { APIHost, NoAuth, DefaultName, DefaultOID, DefaultEmail, DefaultAuthToken } from './../constants'; import { useTranslation } from 'react-i18next'; @@ -25,6 +25,23 @@ const stackStyles: Partial = { } }; +if (NoAuth) { + const data = { + oid: DefaultOID, + name: DefaultName, + email: DefaultEmail + } + sendRequest({ + method: "POST", + headers: { + "Content-Type": 'application/json' + }, + body: JSON.stringify(data), + url: `${APIHost}/users`, + accessToken: DefaultAuthToken + }); +} + export const App: React.FunctionComponent = () => { const { instance } = useMsal(); const { t } = useTranslation(); @@ -32,29 +49,33 @@ export const App: React.FunctionComponent = () => { // This will be run on component mount const callbackId = instance.addEventCallback((message: any) => { // This will be run every time an event is emitted after registering this callback - if (message.eventType === EventType.LOGIN_SUCCESS) { - const result = message.payload; - const tokenClaims = result.account.idTokenClaims - const data = { - oid: tokenClaims.oid, - name: tokenClaims.name, - email: tokenClaims.preferred_username - } - sendRequest({ - method: "POST", - headers: { - "Content-Type": 'application/json' - }, - body: JSON.stringify(data), - url: `${APIHost}/users`, - accessToken: result.accessToken - }) - .then(response => { - window.location.href = '/#/home' - console.log(response) + if (NoAuth) { + console.log("user already added at start"); + } + else { + if (message.eventType === EventType.LOGIN_SUCCESS) { + const result = message.payload; + const tokenClaims = result.account.idTokenClaims + const data = { + oid: tokenClaims.oid, + name: tokenClaims.name, + email: tokenClaims.preferred_username + } + sendRequest({ + method: "POST", + headers: { + "Content-Type": 'application/json' + }, + body: JSON.stringify(data), + url: `${APIHost}/users`, + accessToken: result.accessToken }) - - } + .then(response => { + window.location.href = '/#/home' + console.log(response) + }) + } + } }); return () => { @@ -79,41 +100,74 @@ export const App: React.FunctionComponent = () => { }) } - return ( - - - Welcome to PwR Studio - - - - Programming with Representations (PwR) gives you the ability to build applications using a conversational interface. Build anything you like just by describing it in plain English. + if (NoAuth) { + return ( + + + Welcome to PwR Studio - -
-
- - -
-
+ - Click on My Projects to access your projects + Programming with Representations (PwR) gives you the ability to build applications using a conversational interface. Build anything you like just by describing it in plain English. -
-
-
- - {t('login')} - - - instance.logoutPopup()}>{t('logout')} - -
-
-
    -
-
-
-
- ); +
+
+ +
+
+ + Click on My Projects to access your projects + +
+
+
+
+
+
    +
+
+
+
+ ); + } + else { + return ( + + + Welcome to PwR Studio + + + + Programming with Representations (PwR) gives you the ability to build applications using a conversational interface. Build anything you like just by describing it in plain English. + + +
+
+ + +
+
+ + Click on My Projects to access your projects + +
+
+
+
+ + {t('login')} + + + instance.logoutPopup()}>{t('logout')} + +
+
+
    +
+
+
+
+ ); + } }; \ No newline at end of file diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index 86f8001..765c6c3 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -5,7 +5,7 @@ import { useId, useBoolean } from '@fluentui/react-hooks'; import { Pivot, PivotItem, IPivotStyles, Text } from '@fluentui/react'; import React from 'react'; import { useState } from 'react'; -import { APIHost } from '../constants'; +import { APIHost, NoAuth, DefaultName, DefaultOID, DefaultEmail, DefaultAuthToken } from '../constants'; import { useParams } from 'react-router-dom'; import { sendRequest } from '../api'; import { useAccount, useMsal } from '@azure/msal-react'; @@ -97,29 +97,37 @@ export const EditorPage: React.FunctionComponent = () => { }, [params.id, token, userId]) React.useEffect(() => { - if (account && inProgress === "none") { - instance.acquireTokenSilent({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - account: account - }).then((response) => { - setToken(response.accessToken) - setName(response?.account?.idTokenClaims?.name || 'User') - setUserId(response?.account?.idTokenClaims?.oid) - }).catch((error) => { - console.log(error) - // in case if silent token acquisition fails, fallback to an interactive method - if (error instanceof InteractionRequiredAuthError) { - if (account && inProgress === "none") { - instance.acquireTokenPopup({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - }).then((response) => { - setToken(response.accessToken) - setUserId(response?.account?.idTokenClaims?.oid) - }).catch(error => console.log(error)); - } - } - }); - } + if (NoAuth) { + console.log("Setting dummy user") + setToken(DefaultAuthToken) + setUserId(DefaultOID) + setName(DefaultName) + } + else { + if (account && inProgress === "none") { + instance.acquireTokenSilent({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + account: account + }).then((response) => { + setToken(response.accessToken) + setName(response?.account?.idTokenClaims?.name || 'User') + setUserId(response?.account?.idTokenClaims?.oid) + }).catch((error) => { + console.log(error) + // in case if silent token acquisition fails, fallback to an interactive method + if (error instanceof InteractionRequiredAuthError) { + if (account && inProgress === "none") { + instance.acquireTokenPopup({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + }).then((response) => { + setToken(response.accessToken) + setUserId(response?.account?.idTokenClaims?.oid) + }).catch(error => console.log(error)); + } + } + }); + } + } }, [account, inProgress, instance]); React.useEffect(() => { @@ -336,9 +344,12 @@ export const EditorPage: React.FunctionComponent = () => { }) } - - { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'PowerButton' }} title={t('logout')} ariaLabel={t('logout')} /> - + { + NoAuth ? null : + + { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'PowerButton' }} title={t('logout')} ariaLabel={t('logout')} /> + + } diff --git a/studio/src/pages/home-page.tsx b/studio/src/pages/home-page.tsx index b2f4e48..c0819fe 100644 --- a/studio/src/pages/home-page.tsx +++ b/studio/src/pages/home-page.tsx @@ -7,7 +7,7 @@ import { useId } from '@fluentui/react-hooks'; import { useAccount, useMsal } from '@azure/msal-react'; import { InteractionRequiredAuthError } from '@azure/msal-browser'; import { sendRequest } from '../api'; -import { APIHost } from '../constants'; +import { APIHost, NoAuth, DefaultName, DefaultOID, DefaultEmail, DefaultAuthToken } from '../constants'; import moment from 'moment'; import { appInsights } from '../applicationInsightsService'; import logo from './images/logo.png'; @@ -48,29 +48,36 @@ export const HomePage: React.FunctionComponent = () => { }; React.useEffect(() => { - if (account && inProgress === "none") { - instance.acquireTokenSilent({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - account: account - }).then((response) => { - console.log(response) - setToken(response.accessToken) - setUserId(response?.account?.idTokenClaims?.oid) - }).catch((error) => { - console.log(error) - // in case if silent token acquisition fails, fallback to an interactive method - if (error instanceof InteractionRequiredAuthError) { - if (account && inProgress === "none") { - instance.acquireTokenPopup({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - }).then((response) => { - setToken(response.accessToken) - setUserId(response?.account?.idTokenClaims?.oid) - }).catch(error => console.log(error)); - } - } - }); - } + if (NoAuth) { + console.log("Setting dummy user") + setToken(DefaultAuthToken) + setUserId(DefaultOID) + } + else { + if (account && inProgress === "none") { + instance.acquireTokenSilent({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + account: account + }).then((response) => { + console.log(response) + setToken(response.accessToken) + setUserId(response?.account?.idTokenClaims?.oid) + }).catch((error) => { + console.log(error) + // in case if silent token acquisition fails, fallback to an interactive method + if (error instanceof InteractionRequiredAuthError) { + if (account && inProgress === "none") { + instance.acquireTokenPopup({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + }).then((response) => { + setToken(response.accessToken) + setUserId(response?.account?.idTokenClaims?.oid) + }).catch(error => console.log(error)); + } + } + }); + } + } }, [account, inProgress, instance]); const clickAction = (project: any) => { @@ -399,11 +406,14 @@ export const HomePage: React.FunctionComponent = () => { - - - { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'SignOut' }} title={t('logout')} ariaLabel={t('logout')} /> - - + { + NoAuth ? null : + + + { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'SignOut' }} title={t('logout')} ariaLabel={t('logout')} /> + + + } From b0a481d32931045aa814c35c348960a76e24f36c Mon Sep 17 00:00:00 2001 From: "vachand@microsoft.com" Date: Mon, 24 Jun 2024 20:04:17 +0530 Subject: [PATCH 02/19] test bot ui change --- lib/pwr_studio/engines/__init__.py | 7 ++++--- studio/src/components/Chat/TestBot.tsx | 11 ++++++++++- studio/src/pages/editor-page.tsx | 22 ++++++++++------------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/pwr_studio/engines/__init__.py b/lib/pwr_studio/engines/__init__.py index 5b2cedc..824578e 100644 --- a/lib/pwr_studio/engines/__init__.py +++ b/lib/pwr_studio/engines/__init__.py @@ -46,8 +46,7 @@ def __init__( assert isinstance(kwargs["correlation_id"], str) self._correlation_id = kwargs["correlation_id"] - if len(self._project.representations.keys()) == 0: - self.initalize() + self.initalize() @abstractmethod def _get_representations(self) -> List[PwRStudioRepresentation]: @@ -58,7 +57,9 @@ def initalize(self): for pwrRep in pwrRepresentations: name = pwrRep.get_name() data = pwrRep.get_data() - self._project.representations[name] = data + + if name not in self._project.representations: + self._project.representations[name] = data async def process_utterance(self, text: str, **kwargs): allowed_args = {"chat_history", "files"} diff --git a/studio/src/components/Chat/TestBot.tsx b/studio/src/components/Chat/TestBot.tsx index c216d4d..e2fabc3 100644 --- a/studio/src/components/Chat/TestBot.tsx +++ b/studio/src/components/Chat/TestBot.tsx @@ -13,7 +13,9 @@ interface props { id: string | undefined, token: any, userId: any, - setOnlineState: Function + setOnlineState: Function, + resetChat: bool, + resetChatToggle: Function } const testBotStyles = makeStyles(theme => ({ @@ -228,6 +230,13 @@ const TestBot = (props: props) => { return msg; } + React.useEffect(() => { + if (props.resetChat) { + sendMessageToWss("_reset_chat_"); + props.resetChatToggle(false); + } + }, [props.resetChat]); + return ( diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index 765c6c3..6d8c1e5 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -78,6 +78,7 @@ export const EditorPage: React.FunctionComponent = () => { const [inputText, setInputText] = React.useState(''); const fileInput = React.createRef(); const [dslImportLoader, setDslImportLoader] = React.useState(false); + const [resetTestChat, setResetTestChat] = React.useState(false); React.useEffect(() => { if (token && params.id) { sendRequest({ @@ -268,7 +269,7 @@ export const EditorPage: React.FunctionComponent = () => { } const renderRep = (item: any, index: number) => { - // if (item.name !== 'code') { + if (item.name !== 'fsm_state') { return ( @@ -279,7 +280,7 @@ export const EditorPage: React.FunctionComponent = () => { {item.name} ) - // } + } } return ( @@ -427,15 +428,12 @@ export const EditorPage: React.FunctionComponent = () => { aria-label="Actions" overflowItems={[ { - key: 'download', - name: 'Download Transcripts', - iconProps: { iconName: 'Download' }, - onClick: () => {}, - }, { - key: 'callbackForm', - name: 'Callback form', - iconProps: { iconName: 'FormLibrary' }, - onClick: () => { }, + key: 'clearData', + name: 'Reset session', + iconProps: { iconName: 'Clear' }, + onClick: () => { + setResetTestChat(true); + }, } ]} onRenderOverflowButton={onRenderOverflowButton} @@ -449,7 +447,7 @@ export const EditorPage: React.FunctionComponent = () => { setRefreshIR(refreshIR + 1) } pluginStoreToggle={showPluginStore} userId={userId} setOnlineState={setDevChatStatus} id={params.id} token={token} />
- +
From 45a613af852be495110db3d190d0c9f519ca4678 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Mon, 24 Jun 2024 23:11:19 +0530 Subject: [PATCH 03/19] save test bot state --- server/app/main.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/server/app/main.py b/server/app/main.py index ebb6b5d..4ceefc8 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -973,6 +973,26 @@ async def get_output_response( print( f"#3S - main.py Starting Output Webhook with {output_response.type}:{output_response.message}; PwR-RID={output_response.correlation_id}" ) + + if output_response.project: + project = output_response.project + # if project.current_iteration: + # iterations_row['description'] = project.current_iteration.description + + for name in project.representations: + representation = project.representations[name] + rep_dict = { + "name": name, + "type": representation.type, + "text": representation.text, + "project_id": pid, + "is_pwr_viewable": representation.is_pwr_viewable, + "is_user_viewable": True, + "is_editable": representation.is_editable, + "sort_order": representation.sort_order, + } + crud.create_representation(db, rep_dict) + if output_response.type == "output" or output_response.type == "debug": crud.create_output_message( db, From bf0a50e56e0d2677c81bd73a6b84287b1a18eab9 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 12:42:01 +0530 Subject: [PATCH 04/19] publish modal fix --- studio/src/components/PublishModal/index.tsx | 1 + studio/src/pages/editor-page.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/studio/src/components/PublishModal/index.tsx b/studio/src/components/PublishModal/index.tsx index 6f433ce..a8918f4 100644 --- a/studio/src/components/PublishModal/index.tsx +++ b/studio/src/components/PublishModal/index.tsx @@ -4,6 +4,7 @@ import React from 'react'; interface props { isOpen: boolean hideModal: Function + representations: any } export const PublishModal = (props: props) => { return( diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index 6d8c1e5..acf7ba5 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -286,7 +286,7 @@ export const EditorPage: React.FunctionComponent = () => { return ( - + Date: Tue, 25 Jun 2024 18:47:10 +0530 Subject: [PATCH 05/19] fix publish --- studio/src/components/Chat/TestBot.tsx | 5 +- studio/src/components/PublishModal/index.tsx | 57 +++++++++++++++++--- studio/src/pages/editor-page.tsx | 7 +-- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/studio/src/components/Chat/TestBot.tsx b/studio/src/components/Chat/TestBot.tsx index e2fabc3..ce31d8c 100644 --- a/studio/src/components/Chat/TestBot.tsx +++ b/studio/src/components/Chat/TestBot.tsx @@ -85,6 +85,7 @@ const TestBot = (props: props) => { const [disableSend, setDisableSend] = React.useState(false); const [callBackMode, setCallBackMode] = React.useState(false); + const botRestartmessage = "_reset_chat_" const { getWebSocket, @@ -232,7 +233,7 @@ const TestBot = (props: props) => { React.useEffect(() => { if (props.resetChat) { - sendMessageToWss("_reset_chat_"); + sendMessageToWss(botRestartmessage); props.resetChatToggle(false); } }, [props.resetChat]); @@ -248,7 +249,7 @@ const TestBot = (props: props) => { {messages ? messages.map((message: any, index: string) => ( //
<> - {message.message.trim() !== '' && !['representation_edit', 'files'].includes(message.type) && !(message.type === 'thought' && ['input', 'event'].includes(message.message.trim())) && + {message.message.trim() !== '' && !['representation_edit', 'files'].includes(message.type) && !(message.type === 'thought' && ['input', 'event'].includes(message.message.trim())) && message.message.trim() !== botRestartmessage &&
{ + const dslContent = representations?.find(r => r.name == 'dsl')?.text || '{}' + const codeContent = representations?.find(r => r.name == 'code')?.text || '' + + const keyFields = JSON.parse(dslContent)?.config_vars?.map(x => x?.name).join(',') + + const requestBody = { + 'name': name, + 'dsl': dslContent, + 'code': codeContent, + 'required_credentials': keyFields, + } + + sendRequest({ + url: url, + method: "POST", + accessToken: secret, + body: requestBody + }); +} + export const PublishModal = (props: props) => { + const [message, setMessage] = React.useState(''); + const [secretKey, setSecretKey] = React.useState(''); + + const resetModal = () => { + setMessage(''); + setSecretKey(''); + props.hideModal(); + } + return( <> { - + { setMessage(value || '') }} + value={message}/> @@ -33,7 +70,10 @@ export const PublishModal = (props: props) => { - + { setSecretKey(value || '') }} + value={secretKey}/> @@ -54,10 +94,13 @@ before you do wide-scale release. - props.hideModal()}>Publish + { + uploadProgram(message, secretKey, props.dslName, props.representations); + resetModal(); + }}>Publish - props.hideModal()}>Cancel + resetModal()}>Cancel diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index acf7ba5..e4ad583 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -286,7 +286,7 @@ export const EditorPage: React.FunctionComponent = () => { return ( - + { { setResetTestChat(true); }, From 4d1bc34266af632081bae744ee08104ed18aa91b Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 19:56:46 +0530 Subject: [PATCH 06/19] Update docker-compose.yml --- docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 0d7ac6e..012922e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,8 +31,6 @@ services: - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY} - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION} - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT} - - NO_AUTH=${NO_AUTH} - - DEFAULT_AUTH_OID=1 # Add all Azure OpenAI keys depends_on: - postgres @@ -50,7 +48,6 @@ services: - VITE_REACT_APP_SERVER_HOST=${SERVER_HOST} - VITE_REACT_APP_INSIGHTS_KEY=${APPINSIGHTS_INSTRUMENTATIONKEY} - VITE_REACT_EDITOR=code - - VITE_REACT_APP_NO_AUTH=${NO_AUTH} ports: - "4173:4173" depends_on: From 88303d318d44d2868a0b9aa2a9669be5366d38b0 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 19:58:08 +0530 Subject: [PATCH 07/19] Update main.py --- server/app/main.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 4ceefc8..21aeed3 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -80,9 +80,6 @@ def get_db(): AAD_APP_CLIENT_ID = os.environ["AAD_APP_CLIENT_ID"] AAD_APP_TENANT_ID = os.environ["AAD_APP_TENANT_ID"] # ISSUER = os.environ["ISSUER"] -IS_NO_AUTH = os.environ["NO_AUTH"] -DEFAULT_OID = os.environ["DEFAULT_AUTH_OID"] - configReq = requests.get( f"https://login.microsoftonline.com/{AAD_APP_TENANT_ID}/.well-known/openid-configuration" ) @@ -179,12 +176,7 @@ async def add_process_time_header(req: Request, call_next): return JSONResponse(status_code=401, content="") try: token = req.headers["authorization"] - - verification_of_token = None - if IS_NO_AUTH: - verification_of_token = {"oid": str(DEFAULT_OID)} - else: - verification_of_token = await verify_jwt(token) + verification_of_token = await verify_jwt(token) if verification_of_token: user_id = verification_of_token["oid"] From e4c463db8827d8d48e85fa6760ddb94e589d9790 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 19:58:55 +0530 Subject: [PATCH 08/19] Update constants.ts --- studio/src/constants.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/studio/src/constants.ts b/studio/src/constants.ts index 1c53dee..c5df6ed 100644 --- a/studio/src/constants.ts +++ b/studio/src/constants.ts @@ -1,5 +1,4 @@ const APIHOST = import.meta.env.VITE_REACT_APP_SERVER_HOST; -const NOAUTH = import.meta.env.VITE_REACT_APP_NO_AUTH; const removeProtocol = (url:string) => { return url.replace(/^(https?:\/\/)/, ""); @@ -11,9 +10,3 @@ const host = removeProtocol(APIHOST); export const APIHost = isSecure ? `https://${host}` : `http://${host}`; export const WebSocketHost = isSecure ? `wss://${host}` : `ws://${host}`; -export const NoAuth = NOAUTH ? true : false; - -export const DefaultName = "a"; -export const DefaultOID = "1"; -export const DefaultEmail = "a@b.com"; -export const DefaultAuthToken = "1234"; From 0641cf398a218e39e7c5818b324cec6213824a04 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 20:00:11 +0530 Subject: [PATCH 09/19] Update index.tsx --- studio/src/index.tsx | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/studio/src/index.tsx b/studio/src/index.tsx index 8bf32f3..b0f42ea 100644 --- a/studio/src/index.tsx +++ b/studio/src/index.tsx @@ -13,7 +13,6 @@ import { App, HomePage, EditorPage } from './pages'; import { AppInsightsContext } from '@microsoft/applicationinsights-react-js'; import { reactPlugin } from './applicationInsightsService'; import { msalConfig } from "./authConfig"; -import { NoAuth } from './constants'; import './i18n'; const msalInstance = new PublicClientApplication(msalConfig); @@ -33,18 +32,12 @@ const authenticatedRouter = createHashRouter([ } ]); -let unAuthenticatedRouter = null; -if (NoAuth) { - unAuthenticatedRouter = authenticatedRouter; -} -else { - unAuthenticatedRouter = createHashRouter([ - { - path: "/", - element: , - } - ]); -} +const unAuthenticatedRouter = createHashRouter([ + { + path: "/", + element: , + } +]) // Inject some global styles mergeStyles({ From 4ef31ff6a47022a19490c93e9d284dcc44572581 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 20:03:20 +0530 Subject: [PATCH 10/19] Update app.tsx --- studio/src/pages/app.tsx | 168 +++++++++++++-------------------------- 1 file changed, 57 insertions(+), 111 deletions(-) diff --git a/studio/src/pages/app.tsx b/studio/src/pages/app.tsx index bf0d5c9..ddbbc48 100644 --- a/studio/src/pages/app.tsx +++ b/studio/src/pages/app.tsx @@ -4,7 +4,7 @@ import '../styles/App.css'; import { AuthenticatedTemplate, UnauthenticatedTemplate, useMsal } from "@azure/msal-react"; import { EventType } from "@azure/msal-browser"; import { sendRequest } from './../api'; -import { APIHost, NoAuth, DefaultName, DefaultOID, DefaultEmail, DefaultAuthToken } from './../constants'; +import { APIHost } from './../constants'; import { useTranslation } from 'react-i18next'; @@ -25,23 +25,6 @@ const stackStyles: Partial = { } }; -if (NoAuth) { - const data = { - oid: DefaultOID, - name: DefaultName, - email: DefaultEmail - } - sendRequest({ - method: "POST", - headers: { - "Content-Type": 'application/json' - }, - body: JSON.stringify(data), - url: `${APIHost}/users`, - accessToken: DefaultAuthToken - }); -} - export const App: React.FunctionComponent = () => { const { instance } = useMsal(); const { t } = useTranslation(); @@ -49,33 +32,29 @@ export const App: React.FunctionComponent = () => { // This will be run on component mount const callbackId = instance.addEventCallback((message: any) => { // This will be run every time an event is emitted after registering this callback - if (NoAuth) { - console.log("user already added at start"); - } - else { - if (message.eventType === EventType.LOGIN_SUCCESS) { - const result = message.payload; - const tokenClaims = result.account.idTokenClaims - const data = { - oid: tokenClaims.oid, - name: tokenClaims.name, - email: tokenClaims.preferred_username - } - sendRequest({ - method: "POST", - headers: { - "Content-Type": 'application/json' - }, - body: JSON.stringify(data), - url: `${APIHost}/users`, - accessToken: result.accessToken - }) - .then(response => { - window.location.href = '/#/home' - console.log(response) - }) + if (message.eventType === EventType.LOGIN_SUCCESS) { + const result = message.payload; + const tokenClaims = result.account.idTokenClaims + const data = { + oid: tokenClaims.oid, + name: tokenClaims.name, + email: tokenClaims.preferred_username } - } + sendRequest({ + method: "POST", + headers: { + "Content-Type": 'application/json' + }, + body: JSON.stringify(data), + url: `${APIHost}/users`, + accessToken: result.accessToken + }) + .then(response => { + window.location.href = '/#/home' + console.log(response) + }) + + } }); return () => { @@ -100,74 +79,41 @@ export const App: React.FunctionComponent = () => { }) } - if (NoAuth) { - return ( - - - Welcome to PwR Studio + return ( + + + Welcome to PwR Studio + + + + Programming with Representations (PwR) gives you the ability to build applications using a conversational interface. Build anything you like just by describing it in plain English. - + +
+
+ + +
+
- Programming with Representations (PwR) gives you the ability to build applications using a conversational interface. Build anything you like just by describing it in plain English. + Click on My Projects to access your projects +
+
-
-
- -
-
- - Click on My Projects to access your projects - -
-
-
-
-
-
    -
-
-
-
- ); - } - else { - return ( - - - Welcome to PwR Studio - - - - Programming with Representations (PwR) gives you the ability to build applications using a conversational interface. Build anything you like just by describing it in plain English. - - -
-
- - -
-
- - Click on My Projects to access your projects - -
-
-
-
- - {t('login')} - - - instance.logoutPopup()}>{t('logout')} - -
-
-
    -
-
-
-
- ); - } -}; \ No newline at end of file + + + {t('login')} + + + instance.logoutPopup()}>{t('logout')} + +
+
+
    +
+
+
+
+ ); +}; From 8b68ba66bd0e1d71750f636faf81b9d9cde93299 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 20:03:51 +0530 Subject: [PATCH 11/19] Update home-page.tsx --- studio/src/pages/home-page.tsx | 68 +++++++++++++++------------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/studio/src/pages/home-page.tsx b/studio/src/pages/home-page.tsx index c0819fe..b2f4e48 100644 --- a/studio/src/pages/home-page.tsx +++ b/studio/src/pages/home-page.tsx @@ -7,7 +7,7 @@ import { useId } from '@fluentui/react-hooks'; import { useAccount, useMsal } from '@azure/msal-react'; import { InteractionRequiredAuthError } from '@azure/msal-browser'; import { sendRequest } from '../api'; -import { APIHost, NoAuth, DefaultName, DefaultOID, DefaultEmail, DefaultAuthToken } from '../constants'; +import { APIHost } from '../constants'; import moment from 'moment'; import { appInsights } from '../applicationInsightsService'; import logo from './images/logo.png'; @@ -48,36 +48,29 @@ export const HomePage: React.FunctionComponent = () => { }; React.useEffect(() => { - if (NoAuth) { - console.log("Setting dummy user") - setToken(DefaultAuthToken) - setUserId(DefaultOID) - } - else { - if (account && inProgress === "none") { - instance.acquireTokenSilent({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - account: account - }).then((response) => { - console.log(response) - setToken(response.accessToken) - setUserId(response?.account?.idTokenClaims?.oid) - }).catch((error) => { - console.log(error) - // in case if silent token acquisition fails, fallback to an interactive method - if (error instanceof InteractionRequiredAuthError) { - if (account && inProgress === "none") { - instance.acquireTokenPopup({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - }).then((response) => { - setToken(response.accessToken) - setUserId(response?.account?.idTokenClaims?.oid) - }).catch(error => console.log(error)); - } - } - }); - } - } + if (account && inProgress === "none") { + instance.acquireTokenSilent({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + account: account + }).then((response) => { + console.log(response) + setToken(response.accessToken) + setUserId(response?.account?.idTokenClaims?.oid) + }).catch((error) => { + console.log(error) + // in case if silent token acquisition fails, fallback to an interactive method + if (error instanceof InteractionRequiredAuthError) { + if (account && inProgress === "none") { + instance.acquireTokenPopup({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + }).then((response) => { + setToken(response.accessToken) + setUserId(response?.account?.idTokenClaims?.oid) + }).catch(error => console.log(error)); + } + } + }); + } }, [account, inProgress, instance]); const clickAction = (project: any) => { @@ -406,14 +399,11 @@ export const HomePage: React.FunctionComponent = () => { - { - NoAuth ? null : - - - { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'SignOut' }} title={t('logout')} ariaLabel={t('logout')} /> - - - } + + + { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'SignOut' }} title={t('logout')} ariaLabel={t('logout')} /> + + From 984abd99da250c77b21609df048c8e47d90c54f4 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Tue, 25 Jun 2024 20:05:21 +0530 Subject: [PATCH 12/19] Update editor-page.tsx --- studio/src/pages/editor-page.tsx | 56 ++++++++++++++------------------ 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index e4ad583..62b9099 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -5,7 +5,7 @@ import { useId, useBoolean } from '@fluentui/react-hooks'; import { Pivot, PivotItem, IPivotStyles, Text } from '@fluentui/react'; import React from 'react'; import { useState } from 'react'; -import { APIHost, NoAuth, DefaultName, DefaultOID, DefaultEmail, DefaultAuthToken } from '../constants'; +import { APIHost } from '../constants'; import { useParams } from 'react-router-dom'; import { sendRequest } from '../api'; import { useAccount, useMsal } from '@azure/msal-react'; @@ -98,37 +98,29 @@ export const EditorPage: React.FunctionComponent = () => { }, [params.id, token, userId]) React.useEffect(() => { - if (NoAuth) { - console.log("Setting dummy user") - setToken(DefaultAuthToken) - setUserId(DefaultOID) - setName(DefaultName) - } - else { - if (account && inProgress === "none") { - instance.acquireTokenSilent({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - account: account - }).then((response) => { - setToken(response.accessToken) - setName(response?.account?.idTokenClaims?.name || 'User') - setUserId(response?.account?.idTokenClaims?.oid) - }).catch((error) => { - console.log(error) - // in case if silent token acquisition fails, fallback to an interactive method - if (error instanceof InteractionRequiredAuthError) { - if (account && inProgress === "none") { - instance.acquireTokenPopup({ - scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], - }).then((response) => { - setToken(response.accessToken) - setUserId(response?.account?.idTokenClaims?.oid) - }).catch(error => console.log(error)); - } - } - }); - } - } + if (account && inProgress === "none") { + instance.acquireTokenSilent({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + account: account + }).then((response) => { + setToken(response.accessToken) + setName(response?.account?.idTokenClaims?.name || 'User') + setUserId(response?.account?.idTokenClaims?.oid) + }).catch((error) => { + console.log(error) + // in case if silent token acquisition fails, fallback to an interactive method + if (error instanceof InteractionRequiredAuthError) { + if (account && inProgress === "none") { + instance.acquireTokenPopup({ + scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], + }).then((response) => { + setToken(response.accessToken) + setUserId(response?.account?.idTokenClaims?.oid) + }).catch(error => console.log(error)); + } + } + }); + } }, [account, inProgress, instance]); React.useEffect(() => { From 6f3e2a446c8e33c5a97c0b68fa6dd3143c53fb4c Mon Sep 17 00:00:00 2001 From: Vageesh Date: Wed, 26 Jun 2024 19:12:52 +0530 Subject: [PATCH 13/19] Update installation instructions --- docs/tutorials/quickstart.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/quickstart.md b/docs/tutorials/quickstart.md index 478b7af..226de0b 100644 --- a/docs/tutorials/quickstart.md +++ b/docs/tutorials/quickstart.md @@ -34,6 +34,11 @@ ADD_APP_SCOPE_URI="api:///Users.Create" ISSUER="https://sts.windows.net//" ``` +4. Install libgraphviz-dev +```bash +sudo apt install libgraphviz-dev +``` + ## Instructions 1. **Start Docker:** @@ -44,7 +49,7 @@ ISSUER="https://sts.windows.net//" 2. **Open Bash Terminal:** - For Windows, use WSL2. **Note:** PowerShell will not work. -You need to setup 3 repositories to start the PwR Studio. Follow the instructions below to clone the repositories. Keep the repositories in the same directory as siblings. **Note:** Do not clone in your Windows directory as that will change the line endings. +You need to setup 4 repositories to start the PwR Studio. Follow the instructions below to clone the repositories. Keep the repositories in the same directory as siblings. **Note:** Do not clone in your Windows directory as that will change the line endings. 3. **Clone PwR-Studio Repository:** ```bash @@ -71,16 +76,23 @@ You need to setup 3 repositories to start the PwR Studio. Follow the instruction git clone git@github.com:microsoft/PwR-NL2DSL.git ``` + +6. **Clone [Jugalbandi-Manager](https://github.com/OpenNyAI/Jugalbandi-Manager) Repository:** + ```bash + git clone git@github.com:OpenNyAI/Jugalbandi-Manager.git + ``` + Great job! You have successfully cloned the repositories. 🎉 Your directory structure should look like this: ```bash + ├── Jugalbandi-Manager ├── Jugalbandi-Studio-Engine ├── PwR-NL2DSL └── PwR-Studio ``` -6. **Setup Local Environment Variables:** +7. **Setup Local Environment Variables:** 1. Go into `PwR-Studio` repository ```bash From 224dfbcb96624ba2e6c2bd13fd4711527adf7bf9 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Sat, 6 Jul 2024 15:48:25 +0530 Subject: [PATCH 14/19] update styling --- studio/src/components/Chat/TestBot.tsx | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/studio/src/components/Chat/TestBot.tsx b/studio/src/components/Chat/TestBot.tsx index ce31d8c..394ea84 100644 --- a/studio/src/components/Chat/TestBot.tsx +++ b/studio/src/components/Chat/TestBot.tsx @@ -14,8 +14,8 @@ interface props { token: any, userId: any, setOnlineState: Function, - resetChat: bool, - resetChatToggle: Function + resetChat: bool, + resetChatToggle: Function } const testBotStyles = makeStyles(theme => ({ @@ -36,6 +36,10 @@ const testBotStyles = makeStyles(theme => ({ background: '#AFD8FF', color: '#000' }, + plugin: { + background: '#C9CCCF', + color: '#000' + }, input: { alignSelf: 'flex-end', backgroundColor: '#f0949e', @@ -77,6 +81,7 @@ const TestBot = (props: props) => { const classUser = mergeStyles(classes.message, classes.user); const classInput = mergeStyles(classes.message, classes.input); const classAgent = mergeStyles(classes.message, classes.agent); + const classPlugin = mergeStyles(classes.message, classes.plugin); const userTypes = ["instruction", "feedback"] const noBackground = mergeStyles(classes.message, classes.noBackground) const debugClass = mergeStyles(classes.message, classes.debug); @@ -85,7 +90,7 @@ const TestBot = (props: props) => { const [disableSend, setDisableSend] = React.useState(false); const [callBackMode, setCallBackMode] = React.useState(false); - const botRestartmessage = "_reset_chat_" + const botRestartmessage = "_reset_chat_" const { getWebSocket, @@ -143,7 +148,9 @@ const TestBot = (props: props) => { } if (userTypes.includes(messageType)) { return classUser - } else { + } else if (message.startsWith('##plugin')) { + return classPlugin + } else { return classAgent } } @@ -231,12 +238,12 @@ const TestBot = (props: props) => { return msg; } - React.useEffect(() => { - if (props.resetChat) { - sendMessageToWss(botRestartmessage); - props.resetChatToggle(false); - } - }, [props.resetChat]); + React.useEffect(() => { + if (props.resetChat) { + sendMessageToWss(botRestartmessage); + props.resetChatToggle(false); + } + }, [props.resetChat]); return ( From 6e563e7e625d969d6e98230af46a3b9a08a2280f Mon Sep 17 00:00:00 2001 From: Vageesh Date: Sat, 6 Jul 2024 15:57:56 +0530 Subject: [PATCH 15/19] fix env variable reference --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 012922e..a5ad422 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,7 +44,7 @@ services: - VITE_REACT_APP_AAD_APP_CLIENT_ID=${AAD_APP_CLIENT_ID} - VITE_REACT_APP_AAD_APP_TENANT_ID=${AAD_APP_TENANT_ID} - VITE_REACT_APP_AAD_APP_REDIRECT_URI=${AAD_APP_REDIRECT_URI} - - VITE_REACT_APP_AAD_APP_SCOPE_URI=${AAD_APP_SCOPE_URI} + - VITE_REACT_APP_ADD_APP_SCOPE_URI=${ADD_APP_SCOPE_URI} - VITE_REACT_APP_SERVER_HOST=${SERVER_HOST} - VITE_REACT_APP_INSIGHTS_KEY=${APPINSIGHTS_INSTRUMENTATIONKEY} - VITE_REACT_EDITOR=code From 4ade9bba5ddc608257cda9790010f6a0e04f5691 Mon Sep 17 00:00:00 2001 From: Vageesh Date: Sat, 6 Jul 2024 16:12:57 +0530 Subject: [PATCH 16/19] fix unused code paths --- studio/src/index.tsx | 2 +- studio/src/pages/app.tsx | 2 +- studio/src/pages/editor-page.tsx | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/studio/src/index.tsx b/studio/src/index.tsx index b0f42ea..784f93b 100644 --- a/studio/src/index.tsx +++ b/studio/src/index.tsx @@ -37,7 +37,7 @@ const unAuthenticatedRouter = createHashRouter([ path: "/", element: , } -]) +]); // Inject some global styles mergeStyles({ diff --git a/studio/src/pages/app.tsx b/studio/src/pages/app.tsx index ddbbc48..ebdd36e 100644 --- a/studio/src/pages/app.tsx +++ b/studio/src/pages/app.tsx @@ -116,4 +116,4 @@ export const App: React.FunctionComponent = () => { ); -}; +}; \ No newline at end of file diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index 62b9099..bb6022c 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -337,12 +337,9 @@ export const EditorPage: React.FunctionComponent = () => { }) } - { - NoAuth ? null : - - { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'PowerButton' }} title={t('logout')} ariaLabel={t('logout')} /> - - } + + { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'PowerButton' }} title={t('logout')} ariaLabel={t('logout')} /> + From 08f8e2db00fa77f6e97c222552571462ef3d6c9b Mon Sep 17 00:00:00 2001 From: Vageesh Date: Sat, 6 Jul 2024 16:38:21 +0530 Subject: [PATCH 17/19] fix styling --- studio/src/pages/editor-page.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/studio/src/pages/editor-page.tsx b/studio/src/pages/editor-page.tsx index bb6022c..5ea9634 100644 --- a/studio/src/pages/editor-page.tsx +++ b/studio/src/pages/editor-page.tsx @@ -78,7 +78,7 @@ export const EditorPage: React.FunctionComponent = () => { const [inputText, setInputText] = React.useState(''); const fileInput = React.createRef(); const [dslImportLoader, setDslImportLoader] = React.useState(false); - const [resetTestChat, setResetTestChat] = React.useState(false); + const [resetTestChat, setResetTestChat] = React.useState(false); React.useEffect(() => { if (token && params.id) { sendRequest({ @@ -98,7 +98,7 @@ export const EditorPage: React.FunctionComponent = () => { }, [params.id, token, userId]) React.useEffect(() => { - if (account && inProgress === "none") { + if (account && inProgress === "none") { instance.acquireTokenSilent({ scopes: [import.meta.env.VITE_REACT_APP_ADD_APP_SCOPE_URI || ''], account: account @@ -337,9 +337,9 @@ export const EditorPage: React.FunctionComponent = () => { }) } - - { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'PowerButton' }} title={t('logout')} ariaLabel={t('logout')} /> - + + { instance.logoutPopup(); window.location.href = `#/` } } iconProps={{ iconName: 'PowerButton' }} title={t('logout')} ariaLabel={t('logout')} /> + @@ -415,15 +415,15 @@ export const EditorPage: React.FunctionComponent = () => { { - setResetTestChat(true); - }, + setResetTestChat(true); + }, } ]} onRenderOverflowButton={onRenderOverflowButton} From 797f43874094b3daef1ef2e1c602a205b224bb1c Mon Sep 17 00:00:00 2001 From: Vageesh Date: Sun, 7 Jul 2024 15:31:19 +0530 Subject: [PATCH 18/19] support forms and dropdowns for test bot input --- studio/src/components/Chat/TestBot.tsx | 61 +++++++++++++++---- studio/src/components/DropdownInput/index.tsx | 50 +++++++++++++++ studio/src/components/FormInput/index.tsx | 59 ++++++++++++++++++ studio/src/components/index.tsx | 6 +- 4 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 studio/src/components/DropdownInput/index.tsx create mode 100644 studio/src/components/FormInput/index.tsx diff --git a/studio/src/components/Chat/TestBot.tsx b/studio/src/components/Chat/TestBot.tsx index 394ea84..206e588 100644 --- a/studio/src/components/Chat/TestBot.tsx +++ b/studio/src/components/Chat/TestBot.tsx @@ -7,7 +7,8 @@ import TypingLoader from "./TypingLoader"; import { sendRequest } from '../../api'; import TestBotFooter from "./TestBotFooter"; import { useId } from '@fluentui/react-hooks'; - +import FormInput from '../FormInput'; +import DropdownInput from '../DropdownInput'; interface props { id: string | undefined, @@ -127,7 +128,7 @@ const TestBot = (props: props) => { scrollChatToBottom(); } }, [lastJsonMessage, addMessages]); - + React.useEffect(() => { if (readyState === ReadyState.OPEN) { props.setOnlineState(true) @@ -143,14 +144,14 @@ const TestBot = (props: props) => { if (messageType === 'debug') { return debugClass } - if (message.indexOf('\xa1') > -1) { - return classInput - } + //if (message.indexOf('\xa1') > -1) { + // return classInput + //} if (userTypes.includes(messageType)) { return classUser } else if (message.startsWith('##plugin')) { - return classPlugin - } else { + return classPlugin + } else { return classAgent } } @@ -238,6 +239,37 @@ const TestBot = (props: props) => { return msg; } + const isCustomInputNeeded = (messages:any, type_check:string) => { + if (messages != null && messages.length > 1) { + if (messages[0].type === 'end' + && messages[1].type === 'output' + && messages[1].message.indexOf("\xa1") > -1) { + const msgBody = messages[1].message.split('\xa1')[0]; + const jobj = JSON.parse(msgBody); + return jobj["type"] === type_check; + } + } + return false; + } + + const isFromInputNeeded = (messages:any) => { + return isCustomInputNeeded(messages, "form"); + } + + const getFormFields = (messages:any) => { + const msgBody = messages[1].message.split('\xa1')[0]; + return JSON.parse(msgBody)["vars"] + } + + const isDropdownInputNeeded = (messages:any) => { + return isCustomInputNeeded(messages, "dropdown"); + } + + const getDropdownFields = (messages:any) => { + const msgBody = messages[1].message.split('\xa1')[0]; + return JSON.parse(msgBody)["options"] + } + React.useEffect(() => { if (props.resetChat) { sendMessageToWss(botRestartmessage); @@ -268,16 +300,16 @@ const TestBot = (props: props) => { {message.type === 'thought' && } {message.type === 'error' && } - +
- {message.type === 'output' ? + {message.type === 'output' ?
: getMessage(message.message)}
- +
} @@ -288,7 +320,14 @@ const TestBot = (props: props) => {
- + { isFromInputNeeded(messages) ? + : + ( + isDropdownInputNeeded(messages) ? + : + + ) + } ) diff --git a/studio/src/components/DropdownInput/index.tsx b/studio/src/components/DropdownInput/index.tsx new file mode 100644 index 0000000..65858ff --- /dev/null +++ b/studio/src/components/DropdownInput/index.tsx @@ -0,0 +1,50 @@ +import '@/styles/components/publishModal.scss'; +import { DefaultButton, Dropdown, Label, Stack } from '@fluentui/react'; +import React from 'react'; + +interface props { + choices: any, + sendChoice: Function +} + +export const DropdownInput = (props: props) => { + const optionsList = props.choices ?? []; + const options = optionsList.map((x, i) => ({'key': '_' + i, 'text': x})); + + if (options.length > 0) { + options[0].selected = true; + } + + const [selectedOption, setSelectedOption] = React.useState(0); + + const setChoice = (e, o, i) => { + setSelectedOption(i); + } + + const updateChoice = () => { + if (options.length > 0) + props.sendChoice(optionsList[selectedOption]); + } + + return ( + + + + + + + + + + Submit + + + + + ); +} + +export default DropdownInput; \ No newline at end of file diff --git a/studio/src/components/FormInput/index.tsx b/studio/src/components/FormInput/index.tsx new file mode 100644 index 0000000..9d8d8a4 --- /dev/null +++ b/studio/src/components/FormInput/index.tsx @@ -0,0 +1,59 @@ +import '@/styles/components/publishModal.scss'; +import { DefaultButton, Label, Stack, TextField } from '@fluentui/react'; +import React from 'react'; + +interface props { + variableNames: any, + sendVariableValues: Function +} + +export const FormInput = (props: props) => { + let valueDict = {} + for (var idx = 0; idx < props.variableNames.length; idx++) { + valueDict[idx] = React.useState(''); + } + + const sendFromData = () => { + let formData = {} + formData["type"] = "form" + formData["vars"] = {} + + let simpleVersion = "" + for (var k in valueDict) { + formData["vars"][props.variableNames[k]] = valueDict[k][0]; + simpleVersion += props.variableNames[k] + simpleVersion += " : " + simpleVersion += valueDict[k][0] + simpleVersion += "\n" + } + const stringVersion = JSON.stringify(formData) + "\xa1" + simpleVersion; + props.sendVariableValues(stringVersion); + } + + return ( + + + {props.variableNames.map((vname: string, index: number) => ( + + + + + + { valueDict[index][1](value || '') }} + value={valueDict[index][0]}/> + + + )) + } + + + sendFromData()}>Submit + + + + + ); +} + +export default FormInput; \ No newline at end of file diff --git a/studio/src/components/index.tsx b/studio/src/components/index.tsx index 129e034..7c65acb 100644 --- a/studio/src/components/index.tsx +++ b/studio/src/components/index.tsx @@ -5,6 +5,8 @@ import InPlaceEditing from './InPlaceEditing'; import PluginList from './PluginList'; import DevBot from './Chat/DevBot'; import Mermaid from './Mermaid'; +import FormInput from './FormInput'; +import DropdownInput from './DropdownInput'; export { AudioRecorder, @@ -13,5 +15,7 @@ export { InPlaceEditing, PluginList, DevBot, - Mermaid + Mermaid, + FormInput, + DropdownInput }; \ No newline at end of file From 861a37e653a29d3e293c852ed1f166835652db20 Mon Sep 17 00:00:00 2001 From: kanak8278 Date: Thu, 18 Jul 2024 11:07:31 +0530 Subject: [PATCH 19/19] fetching dsl and finding required fields --- studio/src/components/PublishModal/index.tsx | 24 +++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/studio/src/components/PublishModal/index.tsx b/studio/src/components/PublishModal/index.tsx index b35bfaf..b92702c 100644 --- a/studio/src/components/PublishModal/index.tsx +++ b/studio/src/components/PublishModal/index.tsx @@ -11,23 +11,31 @@ interface props { } const uploadProgram = (url: string, secret: string, name: string, representations: any) => { - const dslContent = representations?.find(r => r.name == 'dsl')?.text || '{}' + const dslContentText = representations?.find(r => r.name == 'dsl')?.text || '{}'; const codeContent = representations?.find(r => r.name == 'code')?.text || '' - - const keyFields = JSON.parse(dslContent)?.config_vars?.map(x => x?.name).join(',') - + const dslContent = JSON.parse(dslContentText); + const credentials = dslContent.config_vars.map(x => x.name); + ; + const requestBody = { - 'name': name, - 'dsl': dslContent, + 'name': dslContent.fsm_name, + 'dsl': dslContentText, 'code': codeContent, - 'required_credentials': keyFields, + 'requirements': '', + 'required_credentials': credentials, + 'index_urls':[''], + 'version': '1.0.0' + } sendRequest({ url: url, method: "POST", accessToken: secret, - body: requestBody + body: JSON.stringify(requestBody), + headers: { + 'Content-Type': 'application/json' + } }); }