From cf29c85449013f4cd08138a8ef2d3fca1994fab7 Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Tue, 1 Apr 2025 22:05:04 -0700 Subject: [PATCH] Tool and RAG is working and displaying results --- src/ketr-chat/package-lock.json | 48 +++++ src/ketr-chat/package.json | 1 + src/ketr-chat/public/disable-jpk.png | Bin 0 -> 4983 bytes src/ketr-chat/public/settings.png | Bin 0 -> 4846 bytes src/ketr-chat/src/App.css | 70 ++++--- src/ketr-chat/src/App.tsx | 263 +++++++++++++++++++-------- src/server.py | 216 +++++++++++++--------- src/tools.py | 24 +-- src/utils/rag.py | 16 +- 9 files changed, 437 insertions(+), 201 deletions(-) create mode 100755 src/ketr-chat/public/disable-jpk.png create mode 100755 src/ketr-chat/public/settings.png diff --git a/src/ketr-chat/package-lock.json b/src/ketr-chat/package-lock.json index 13e9047..ac4a5ee 100644 --- a/src/ketr-chat/package-lock.json +++ b/src/ketr-chat/package-lock.json @@ -21,6 +21,7 @@ "@types/node": "^16.18.126", "@types/react": "^19.0.12", "@types/react-dom": "^19.0.4", + "mui-markdown": "^1.2.6", "react": "^19.0.0", "react-dom": "^19.0.0", "react-markdown": "^10.1.0", @@ -4626,6 +4627,12 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "optional": true + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -13453,6 +13460,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/markdown-to-jsx": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.7.4.tgz", + "integrity": "sha512-1bSfXyBKi+EYS3YY+e0Csuxf8oZ3decdfhOav/Z7Wrk89tjudyL5FOmwZQUoy0/qVXGUl+6Q3s2SWtpDEWITfQ==", + "peer": true, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -14517,6 +14536,22 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/mui-markdown": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/mui-markdown/-/mui-markdown-1.2.6.tgz", + "integrity": "sha512-GR3+CVLDS4eSri8d4QFkcrBtdBNUCRbWeyXuf3v/t0qZoWDta1U5Wm/MHsYsWSR4HBy6BVoyxlZ0fMNGOCScyQ==", + "optionalDependencies": { + "prism-react-renderer": "^2.0.3" + }, + "peerDependencies": { + "@emotion/react": "^11.10.8", + "@emotion/styled": "^11.10.8", + "@mui/material": ">= 5.12.2", + "markdown-to-jsx": "^7.3.0", + "react": ">= 17.0.2", + "react-dom": ">= 17.0.2" + } + }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -16752,6 +16787,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prism-react-renderer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", + "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", + "optional": true, + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, "node_modules/probe-image-size": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", diff --git a/src/ketr-chat/package.json b/src/ketr-chat/package.json index 811982d..ef15fe7 100644 --- a/src/ketr-chat/package.json +++ b/src/ketr-chat/package.json @@ -16,6 +16,7 @@ "@types/node": "^16.18.126", "@types/react": "^19.0.12", "@types/react-dom": "^19.0.4", + "mui-markdown": "^1.2.6", "react": "^19.0.0", "react-dom": "^19.0.0", "react-markdown": "^10.1.0", diff --git a/src/ketr-chat/public/disable-jpk.png b/src/ketr-chat/public/disable-jpk.png new file mode 100755 index 0000000000000000000000000000000000000000..826227cb3d254dfc1a71c19ea4a6372fdddd29b3 GIT binary patch literal 4983 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 div { @@ -129,42 +145,50 @@ div { } /* Reduce general whitespace in markdown content */ -.markdown-content p { +.assistant-message p.MuiTypography-root { margin-top: 0.5rem; margin-bottom: 0.5rem; + font-size: 0.9rem; } /* Reduce space between headings and content */ -.markdown-content h1, -.markdown-content h2, -.markdown-content h3, -.markdown-content h4, -.markdown-content h5, -.markdown-content h6 { +.assistant-message h1.MuiTypography-root, +.assistant-message h2.MuiTypography-root, +.assistant-message h3.MuiTypography-root, +.assistant-message h4.MuiTypography-root, +.assistant-message h5.MuiTypography-root, +.assistant-message h6.MuiTypography-root { margin-top: 1rem; margin-bottom: 0.5rem; + font-size: 1rem; } /* Reduce space in lists */ -.markdown-content ul, -.markdown-content ol { +.assistant-message ul.MuiTypography-root, +.assistant-message ol.MuiTypography-root { margin-top: 0.5rem; margin-bottom: 0.5rem; - padding-left: 1.5rem; + font-size: 0.9rem; } -.markdown-content li { +.assistant-message li.MuiTypography-root { margin-bottom: 0.25rem; + font-size: 0.9rem; } -/* Reduce space between list items */ -.markdown-content li p { +.assistant-message .MuiTypography-root li { margin-top: 0; margin-bottom: 0; + padding: 0; + font-size: 0.9rem; } /* Reduce space around code blocks */ -.markdown-content pre { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} \ No newline at end of file +.assistant-message .MuiTypography-root pre { + border: 1px solid #F5F5F5; + border-radius: 0.5rem; + padding: 0.5rem 0.75rem; + margin-top: 0; + margin-bottom: 0; + font-size: 0.9rem; +} diff --git a/src/ketr-chat/src/App.tsx b/src/ketr-chat/src/App.tsx index 14a9f71..11bf9cb 100644 --- a/src/ketr-chat/src/App.tsx +++ b/src/ketr-chat/src/App.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef, useCallback, ReactElement } from 'r import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; import { useTheme } from '@mui/material'; +import { styled } from '@mui/material/styles'; import Switch from '@mui/material/Switch'; import Divider from '@mui/material/Divider'; import Tooltip from '@mui/material/Tooltip'; @@ -13,21 +14,29 @@ import AccordionActions from '@mui/material/AccordionActions'; import AccordionSummary from '@mui/material/AccordionSummary'; import AccordionDetails from '@mui/material/AccordionDetails'; import Typography from '@mui/material/Typography'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import Button from '@mui/material/Button'; import AppBar from '@mui/material/AppBar'; import Drawer from '@mui/material/Drawer'; import Toolbar from '@mui/material/Toolbar'; import MenuIcon from '@mui/icons-material/Menu'; import SettingsIcon from '@mui/icons-material/Settings'; -import IconButton from '@mui/material/IconButton'; +import IconButton, { IconButtonProps } from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import CssBaseline from '@mui/material/CssBaseline'; import AddIcon from '@mui/icons-material/AddCircle'; import SendIcon from '@mui/icons-material/Send'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import Card from '@mui/material/Card'; +import CardHeader from '@mui/material/CardHeader'; +import CardMedia from '@mui/material/CardMedia'; +import CardContent from '@mui/material/CardContent'; +import CardActions from '@mui/material/CardActions'; +import Collapse from '@mui/material/Collapse'; import PropagateLoader from "react-spinners/PropagateLoader"; -import Markdown from 'react-markdown'; +// import Markdown from 'react-markdown'; +import { MuiMarkdown as Markdown } from "mui-markdown"; import './App.css'; import rehypeKatex from 'rehype-katex' import remarkMath from 'remark-math' @@ -38,19 +47,26 @@ import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css'; -const welcomeMessage = { "role": "assistant", "content": "Welcome to Ketr-Chat. I have real-time access to a lot of information. Ask things like 'What are the headlines from cnn.com?' or 'What is the weather in Portland, OR?'" }; +const welcomeMarkdown = ` +# Welcome to Ketr-Chat. + +This system has real-time access to weather, stocks, the current time, and can answer questions about the contents of a website. + +**NOTE**: As of right now, the LLM model being used is refusing to use enabled tools when RAG is enabled to provide context. +So, in order to use the real-time information, you need to click the Settings ![settings](settings.png) icon, open RAG, and disable JPK: ![disable JPK](disable-jpk.png). + +Ask things like: + * What are the headlines from CNBC? + * What is the weather in Portland, OR? + * What is James Ketrenos' work history? + * What are the stock value of the most traded companies? +`; + +const welcomeMessage = { + "role": "assistant", "content": welcomeMarkdown +}; const loadingMessage = { "role": "assistant", "content": "Instancing chat session..." }; -//const url: string = "https://ai.ketrenos.com" - -const getConnectionBase = (loc: any): string => { - if (!loc.host.match(/.*battle-linux.*/)) { - return loc.protocol + "//" + loc.host; - } else { - return loc.protocol + "//battle-linux.ketrenos.com:5000"; - } -} - type Tool = { type: string, function?: { @@ -88,6 +104,32 @@ type SystemInfo = { "CPU": string }; +type MessageMetadata = { + rag: any, + tools: any[] +}; + +type MessageData = { + role: string, + content: string, + user?: string, + type?: string, + id?: string, + isProcessing?: boolean, + metadata?: MessageMetadata +}; + +type MessageList = MessageData[]; + + +const getConnectionBase = (loc: any): string => { + if (!loc.host.match(/.*battle-linux.*/)) { + return loc.protocol + "//" + loc.host; + } else { + return loc.protocol + "//battle-linux.ketrenos.com:5000"; + } +} + const SystemInfoComponent: React.FC<{ systemInfo: SystemInfo }> = ({ systemInfo }) => { const [systemElements, setSystemElements] = useState([]); @@ -236,6 +278,130 @@ const Controls = ({ tools, rags, systemPrompt, toggleTool, toggleRag, setSystemP ); } +interface ExpandMoreProps extends IconButtonProps { + expand: boolean; +} + +const ExpandMore = styled((props: ExpandMoreProps) => { + const { expand, ...other } = props; + return ; +})(({ theme }) => ({ + marginLeft: 'auto', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shortest, + }), + variants: [ + { + props: ({ expand }) => !expand, + style: { + transform: 'rotate(0deg)', + }, + }, + { + props: ({ expand }) => !!expand, + style: { + transform: 'rotate(180deg)', + }, + }, + ], +})); + +interface MessageInterface { + message: MessageData +}; + +interface MessageMetaInterface { + metadata: MessageMetadata +} +const MessageMeta = ({ metadata }: MessageMetaInterface) => { + if (metadata === undefined) { + return <> + } + + console.log(JSON.stringify(metadata.tools[0].result, null, 2)); + + return (<> + { + metadata.tools !== undefined && + +

Tools queried:

+ {metadata.tools.map((tool: any, index: number) => <> + + +
+
{tool.tool}
+
Result Len: {JSON.stringify(tool.result).length}
+
+
{JSON.stringify(tool.result, null, 2)}
+
+ )} +
+ } + { + metadata.rag.name !== undefined && + +

RAG from '{metadata.rag.name}' collection matches against embedding vector of {metadata.rag.query_embedding.length} dimensions:

+ {metadata.rag.ids.map((id: number, index: number) => <> + + +
+
Doc ID: {metadata.rag.ids[index]}
+
Similarity: {Math.round(metadata.rag.distances[index] * 100) / 100}
+
Type: {metadata.rag.metadatas[index].doc_type}
+
Chunk Len: {metadata.rag.documents[index].length}
+
+
{metadata.rag.documents[index]}
+
+ + )} +
+ } + + ); +}; + +const Message = ({ message }: MessageInterface) => { + const [expanded, setExpanded] = React.useState(false); + + const handleExpandClick = () => { + setExpanded(!expanded); + }; + + const formattedContent = message.content.trim(); + + return ( + + + {message.role === 'assistant' ? + + : + + {message.content} + + } + + {message.metadata && <> + + LLM information for this query + + + + + + + + + + } + + ); +} + const App = () => { const [query, setQuery] = useState(''); const [conversation, setConversation] = useState([]); @@ -256,8 +422,9 @@ const App = () => { // Scroll to bottom of conversation when conversation updates useEffect(() => { - if (conversationRef.current) { - conversationRef.current.scrollTop = conversationRef.current.scrollHeight; + const queryElement = document.getElementById('QueryInput'); + if (queryElement) { + queryElement.scrollIntoView(); } }, [conversation]); @@ -575,22 +742,6 @@ const App = () => { } }; - type MessageMetadata = { - title: string - }; - - type Message = { - role: string, - content: string, - user?: string, - type?: string, - id?: string, - isProcessing?: boolean, - metadata?: MessageMetadata - }; - - type MessageList = Message[]; - const onNew = async () => { reset(["history"], "New chat started."); } @@ -739,11 +890,13 @@ const App = () => { setSnackOpen(false); }; + const Offset = styled('div')(({ theme }) => theme.mixins.toolbar); + return ( theme.zIndex.drawer + 1, }} @@ -778,6 +931,8 @@ const App = () => { + + { {drawer} - - - {conversation.map((message, index) => { - const formattedContent = message.content.trim(); - - return ( -
- {message.metadata ? ( - <> -
{message.metadata.title}
- {message.user && ( -
{message.user}
- )} - {message.role === 'assistant' ? ( -
- - {/* */} -
- ) : ( -
{formattedContent}
- )} - - ) : ( - <> - {message.user && ( -
{message.user}
- )} - {message.role === 'assistant' ? ( -
- -
- ) : ( -
{formattedContent}
- )} - - )} -
- ); - })} + + + {conversation.map((message, index) => )}