Skip to content

Commit

Permalink
Merge branch 'debug_messages' into 'master'
Browse files Browse the repository at this point in the history
Bot UI: Display debug messages

See merge request postgres-ai/database-lab!867
  • Loading branch information
Bogdan Tsechoev committed Jun 14, 2024
2 parents da38a60 + 89ddffa commit 3024477
Show file tree
Hide file tree
Showing 16 changed files with 486 additions and 197 deletions.
39 changes: 39 additions & 0 deletions ui/packages/platform/src/api/bot/getDebugMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {request} from "../../helpers/request";
import { DebugMessage } from "../../types/api/entities/bot";

type Req =
| { thread_id: string; message_id?: string }
| { thread_id?: string; message_id: string };

export const getDebugMessages = async (req: Req): Promise<{ response: DebugMessage[] | null; error: Response | null }> => {
const { thread_id, message_id } = req;

const params: { [key: string]: string } = {};

if (thread_id) {
params['chat_thread_id'] = `eq.${thread_id}`;
}

if (message_id) {
params['chat_msg_id'] = `eq.${message_id}`;
}

const queryString = new URLSearchParams(params).toString();

const apiServer = process.env.REACT_APP_API_URL_PREFIX || '';

try {
const response = await request(`${apiServer}/chat_debug_messages?${queryString}`);

if (!response.ok) {
return { response: null, error: response };
}

const responseData: DebugMessage[] = await response.json();

return { response: responseData, error: null };

} catch (error) {
return { response: null, error: error as Response };
}
}
5 changes: 4 additions & 1 deletion ui/packages/platform/src/pages/Bot/BotWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BotPage } from "./index";
import {RouteComponentProps} from "react-router";
import {AlertSnackbarProvider} from "@postgres.ai/shared/components/AlertSnackbar/useAlertSnackbar";
import { AiBotProvider } from "./hooks";

export interface BotWrapperProps {
envData: {
Expand All @@ -25,7 +26,9 @@ export interface BotWrapperProps {
export const BotWrapper = (props: BotWrapperProps) => {
return (
<AlertSnackbarProvider>
<BotPage {...props} />
<AiBotProvider args={{ threadId: props.match.params.threadId, orgId: props.orgData.id }}>
<BotPage {...props} />
</AiBotProvider>
</AlertSnackbarProvider>
)
}
13 changes: 5 additions & 8 deletions ui/packages/platform/src/pages/Bot/ChatsList/ChatsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const useStyles = makeStyles<Theme, ChatsListProps>((theme) => ({
[theme.breakpoints.down('sm')]: {
height: '100vh!important',
marginTop: '0!important',
width: 260,
width: 300,
zIndex: 9999
},
'& > ul': {
Expand Down Expand Up @@ -94,7 +94,6 @@ type ChatsListProps = {
loading: boolean;
chatsList: BotMessage[] | null;
onLinkClick?: (targetThreadId: string) => void;
permalinkId?: string
} & HeaderButtonsProps

export const ChatsList = (props: ChatsListProps) => {
Expand All @@ -104,11 +103,10 @@ export const ChatsList = (props: ChatsListProps) => {
onClose,
chatsList,
loading,
currentVisibility,
withChatVisibilityButton,
onChatVisibilityClick,
onSettingsClick,
onLinkClick,
permalinkId
onConsoleClick
} = props;
const classes = useStyles(props);
const params = useParams<{ org?: string, threadId?: string }>();
Expand Down Expand Up @@ -150,10 +148,9 @@ export const ChatsList = (props: ChatsListProps) => {
onClose={onClose}
onCreateNewChat={onCreateNewChat}
isOpen={isOpen}
currentVisibility={currentVisibility}
withChatVisibilityButton={withChatVisibilityButton}
onChatVisibilityClick={onChatVisibilityClick}
permalinkId={permalinkId}
onSettingsClick={onSettingsClick}
onConsoleClick={onConsoleClick}
/>
<Divider/>
</ListSubheader>
Expand Down
39 changes: 30 additions & 9 deletions ui/packages/platform/src/pages/Bot/Command/Command.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react'
import React, { useState, useRef, useEffect, useMemo } from 'react'
import { makeStyles } from '@material-ui/core'
import SendRoundedIcon from '@material-ui/icons/SendRounded';
import IconButton from "@material-ui/core/IconButton";
Expand All @@ -14,12 +14,13 @@ import { useBuffer } from './useBuffer'
import { useCaret } from './useCaret'
import { theme } from "@postgres.ai/shared/styles/theme";
import { isMobileDevice } from "../../../utils/utils";
import { useAiBot } from "../hooks";
import { ReadyState } from "react-use-websocket";


type Props = {
sendDisabled: boolean
onSend: (value: string) => void
threadId?: string
orgId: number | null
}


Expand Down Expand Up @@ -74,10 +75,22 @@ const useStyles = makeStyles((theme) => (
)

export const Command = React.memo((props: Props) => {
const { sendDisabled, onSend, threadId } = props
const { threadId, orgId } = props

const classes = useStyles()
const isMobile = isMobileDevice();

const {
error,
wsReadyState,
wsLoading,
loading,
sendMessage,
chatVisibility
} = useAiBot();

const sendDisabled = error !== null || loading || wsLoading || wsReadyState !== ReadyState.OPEN;

// Handle value.
const [value, setValue] = useState('')

Expand All @@ -90,10 +103,19 @@ export const Command = React.memo((props: Props) => {
// Input caret.
const caret = useCaret(inputRef)

const triggerSend = () => {
const onSend = async (message: string) => {
await sendMessage({
content: message,
thread_id: threadId || null,
org_id: orgId,
is_public: chatVisibility === 'public'
})
}

const triggerSend = async () => {
if (!value.trim().length || sendDisabled) return

onSend(value)
await onSend(value)
buffer.addNew()
setValue(buffer.getCurrent())
}
Expand Down Expand Up @@ -122,13 +144,13 @@ export const Command = React.memo((props: Props) => {
}
}

const handleKeyDown = (e: React.KeyboardEvent) => {
const handleKeyDown = async (e: React.KeyboardEvent) => {
if (!inputRef.current) return

// Trigger to send.
if (checkIsSendCmd(e.nativeEvent)) {
e.preventDefault()
triggerSend()
await triggerSend()
return
}

Expand Down Expand Up @@ -171,7 +193,6 @@ export const Command = React.memo((props: Props) => {
setValue('')
}, [threadId]);


return (
<div className={classes.root}>
<TextField
Expand Down
107 changes: 107 additions & 0 deletions ui/packages/platform/src/pages/Bot/DebugConsole/DebugConsole.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useEffect, useRef, useState } from "react";
import Dialog from "@material-ui/core/Dialog";
import { DialogContent, DialogTitle, makeStyles } from "@material-ui/core";
import { useAiBot } from "../hooks";
import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";
import ReactMarkdown from "react-markdown";
import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close";
import { disallowedHtmlTagsForMarkdown } from "../utils";
import { DebugLogs } from "../DebugLogs/DebugLogs";
import { DebugMessage } from "../../../types/api/entities/bot";

const useStyles = makeStyles(
(theme) => ({
dialogStyle: {
top: '5%!important',
right: '10%!important',
left: 'unset!important',
height: 'fit-content',
width: 'fit-content',
},
paper: {
width: '80vw',
height: '70vh',
opacity: '.5',
transition: '.2s ease',
'&:hover': {
opacity: 1
}
},
dialogTitle: {
padding: '0.5rem',
'& h2': {
fontSize: '1rem',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}
},
dialogContent: {
padding: 0,
margin: 0,
}
}))

type DebugConsoleProps = {
isOpen: boolean
onClose: () => void
threadId?: string
}

export const DebugConsole = (props: DebugConsoleProps) => {
const { isOpen, onClose, threadId } = props;
const { debugMessages, debugMessagesLoading } = useAiBot();
const classes = useStyles();
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (containerRef.current && debugMessages?.length && threadId && !debugMessagesLoading && isOpen) {
let code: HTMLElement = containerRef.current.getElementsByTagName('code')?.[0];
if (code.hasChildNodes()) {
code.appendChild(document.createTextNode(`[${debugMessages[debugMessages.length - 1].created_at}]: ${debugMessages[debugMessages.length - 1].content}\n`))
} else {
debugMessages.forEach((item) => {
code.appendChild(document.createTextNode(`[${item.created_at}]: ${item.content}\n`))
})
const container = document.getElementById(`logs-container-${threadId}`);
if (container) {
container.appendChild(code)
}
}
}
}, [debugMessages, isOpen, threadId, debugMessagesLoading]);

return (
<Dialog
open={isOpen}
disableEnforceFocus
hideBackdrop
className={classes.dialogStyle}
classes={{paper: classes.paper}}
scroll="paper"
fullWidth
maxWidth="xl"
>
<DialogTitle className={classes.dialogTitle}>
Debug console
<IconButton
onClick={onClose}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent
classes={{root: classes.dialogContent}}
ref={containerRef}
>
<DebugLogs
isLoading={debugMessagesLoading}
isEmpty={debugMessages?.length === 0}
id={threadId!}
/>
</DialogContent>
</Dialog>
)
}
Loading

0 comments on commit 3024477

Please sign in to comment.