diff --git a/dewey/app/globals.css b/dewey/app/globals.css
index 13d40b8..8a87c98 100644
--- a/dewey/app/globals.css
+++ b/dewey/app/globals.css
@@ -3,25 +3,62 @@
@tailwind utilities;
:root {
- --background: #ffffff;
- --foreground: #171717;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: #0a0a0a;
- --foreground: #ededed;
- }
+ --foreground-rgb: 255, 255, 255;
+ --background-start-rgb: 0, 0, 0;
+ --background-end-rgb: 0, 0, 0;
}
body {
- color: var(--foreground);
- background: var(--background);
- font-family: Arial, Helvetica, sans-serif;
+ color: rgb(var(--foreground-rgb));
+ background: linear-gradient(
+ to bottom,
+ transparent,
+ rgb(var(--background-end-rgb))
+ )
+ rgb(var(--background-start-rgb));
+ font-family: 'Noto Sans Mono', monospace;
}
-@layer utilities {
- .text-balance {
- text-wrap: balance;
- }
+/* Custom styles from the original index.html */
+.thinking-section {
+ margin-bottom: 20px;
+ border-left: 2px solid #444;
+ padding-left: 10px;
}
+
+.thought-summary {
+ font-weight: bold;
+ margin-bottom: 5px;
+ padding: 5px;
+ border-radius: 3px;
+}
+
+.thought-summary.plan { background-color: #2c3e50; }
+.thought-summary.decision { background-color: #34495e; }
+.thought-summary.tool_call { background-color: #16a085; }
+.thought-summary.tool_result { background-color: #27ae60; }
+.thought-summary.think_more { background-color: #2980b9; }
+.thought-summary.answer { background-color: #8e44ad; }
+
+.thought-details {
+ display: none;
+ margin-left: 20px;
+ border-left: 2px solid #444;
+ padding-left: 10px;
+ margin-bottom: 10px;
+ white-space: pre-wrap;
+ font-family: 'Noto Sans Mono', monospace;
+ background-color: #222;
+}
+
+.collapsible::before {
+ content: '▶ ';
+ display: inline-block;
+ transition: transform 0.3s;
+}
+
+.collapsible.open::before {
+ transform: rotate(90deg);
+}
+
+/* Add any other custom styles from the original index.html here */
diff --git a/dewey/app/layout.tsx b/dewey/app/layout.tsx
index a36cde0..be4d3f0 100644
--- a/dewey/app/layout.tsx
+++ b/dewey/app/layout.tsx
@@ -1,35 +1,31 @@
-import type { Metadata } from "next";
-import localFont from "next/font/local";
-import "./globals.css";
+import './globals.css'
+import { Inter } from 'next/font/google'
-const geistSans = localFont({
- src: "./fonts/GeistVF.woff",
- variable: "--font-geist-sans",
- weight: "100 900",
-});
-const geistMono = localFont({
- src: "./fonts/GeistMonoVF.woff",
- variable: "--font-geist-mono",
- weight: "100 900",
-});
+const inter = Inter({ subsets: ['latin'] })
-export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
-};
+export const metadata = {
+ title: 'DWS Intelligence',
+ description: 'AI-powered chat application',
+}
export default function RootLayout({
children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
+}: {
+ children: React.ReactNode
+}) {
return (
-
- {children}
-
+
+
+
+
+
+
+
+
-
-
-
- -
- Get started by editing{" "}
-
- app/page.tsx
-
- .
-
- - Save and see your changes instantly.
-
+ const [currentChatId, setCurrentChatId] = useState(null)
+ const [chats, setChats] = useLocalStorage('chats', {})
+ const socket = useSocket()
-
-
-
+ useEffect(() => {
+ if (Object.keys(chats).length === 0) {
+ createNewChat()
+ } else {
+ setCurrentChatId(Object.keys(chats)[0])
+ }
+ }, [])
+
+ const createNewChat = () => {
+ const chatId = Date.now().toString()
+ setChats(prevChats => ({
+ ...prevChats,
+ [chatId]: { messages: [], thinkingSections: [] }
+ }))
+ setCurrentChatId(chatId)
+ }
+
+ return (
+
+
+
- );
+ )
}
diff --git a/dewey/components/ChatArea.tsx b/dewey/components/ChatArea.tsx
new file mode 100644
index 0000000..7c3742b
--- /dev/null
+++ b/dewey/components/ChatArea.tsx
@@ -0,0 +1,133 @@
+import { useState, useEffect } from 'react'
+import ChatTabs from './ChatTabs'
+import ChatContainer from './ChatContainer'
+import UserInput from './UserInput'
+
+export default function ChatArea({ currentChatId, setCurrentChatId, chats, setChats, createNewChat, socket }) {
+ const [userInput, setUserInput] = useState('')
+
+ const sendMessage = () => {
+ if (userInput.trim() && currentChatId) {
+ const newMessage = { content: userInput, isUser: true }
+ setChats(prevChats => ({
+ ...prevChats,
+ [currentChatId]: {
+ ...prevChats[currentChatId],
+ messages: [...prevChats[currentChatId].messages, newMessage],
+ thinkingSections: [...prevChats[currentChatId].thinkingSections, { thoughts: [] }]
+ }
+ }))
+ socket.emit('chat_request', {
+ message: userInput,
+ conversation_history: chats[currentChatId].messages
+ .filter(m => !m.isUser).map(m => ({ role: 'assistant', content: m.content }))
+ .concat(chats[currentChatId].messages.filter(m => m.isUser).map(m => ({ role: 'user', content: m.content })))
+ })
+ setUserInput('')
+ }
+ }
+
+ const switchToChat = (chatId: string) => {
+ setCurrentChatId(chatId);
+ }
+
+ const closeChat = (chatId: string) => {
+ if (window.confirm('Are you sure you want to close this chat?')) {
+ setChats(prevChats => {
+ const newChats = { ...prevChats };
+ delete newChats[chatId];
+ return newChats;
+ });
+ if (currentChatId === chatId) {
+ const remainingChatIds = Object.keys(chats).filter(id => id !== chatId);
+ if (remainingChatIds.length > 0) {
+ switchToChat(remainingChatIds[0]);
+ } else {
+ createNewChat();
+ }
+ }
+ }
+ }
+
+ useEffect(() => {
+ if (socket) {
+ socket.on('thinking', (data) => {
+ // Handle thinking event
+ setChats(prevChats => ({
+ ...prevChats,
+ [currentChatId]: {
+ ...prevChats[currentChatId],
+ thinkingSections: [
+ ...prevChats[currentChatId].thinkingSections,
+ { thoughts: [{ type: 'thinking', content: data.step }] }
+ ]
+ }
+ }));
+ });
+
+ socket.on('thought', (data) => {
+ // Handle thought event
+ setChats(prevChats => ({
+ ...prevChats,
+ [currentChatId]: {
+ ...prevChats[currentChatId],
+ thinkingSections: prevChats[currentChatId].thinkingSections.map((section, index) =>
+ index === prevChats[currentChatId].thinkingSections.length - 1
+ ? { ...section, thoughts: [...section.thoughts, data] }
+ : section
+ )
+ }
+ }));
+ });
+
+ socket.on('chat_response', (data) => {
+ // Handle chat response event
+ setChats(prevChats => ({
+ ...prevChats,
+ [currentChatId]: {
+ ...prevChats[currentChatId],
+ messages: [...prevChats[currentChatId].messages, { content: data.response, isUser: false }]
+ }
+ }));
+ });
+
+ socket.on('error', (data) => {
+ // Handle error event
+ console.error('Error:', data.message);
+ // You might want to display this error to the user
+ });
+ }
+
+ return () => {
+ if (socket) {
+ socket.off('thinking');
+ socket.off('thought');
+ socket.off('chat_response');
+ socket.off('error');
+ }
+ };
+ }, [socket, currentChatId, setChats]);
+
+ return (
+
+
+ {currentChatId && (
+
+ )}
+
+
+ )
+}
\ No newline at end of file
diff --git a/dewey/components/ChatContainer.tsx b/dewey/components/ChatContainer.tsx
new file mode 100644
index 0000000..9c3b1a9
--- /dev/null
+++ b/dewey/components/ChatContainer.tsx
@@ -0,0 +1,85 @@
+import React, { useEffect, useRef } from 'react';
+import { marked } from 'marked';
+
+interface ChatContainerProps {
+ currentChat: {
+ messages: Array<{ content: string; isUser: boolean }>;
+ thinkingSections: Array<{ thoughts: Array<{ type: string; content: string; details?: string }> }>;
+ } | null;
+ socket: any;
+}
+
+const ChatContainer: React.FC
= ({ currentChat, socket }) => {
+ const chatContainerRef = useRef(null);
+
+ useEffect(() => {
+ if (chatContainerRef.current) {
+ chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
+ }
+ }, [currentChat]);
+
+ if (!currentChat) return null;
+
+ return (
+
+ {currentChat.messages.map((message, index) => (
+
+
+ {message.isUser ? (
+ message.content
+ ) : (
+
+ )}
+
+
+ ))}
+ {currentChat.thinkingSections.map((section, sectionIndex) => (
+
+ {section.thoughts.map((thought, thoughtIndex) => (
+
+
+ {thought.type}:
+
+
+ {thought.details && (
+
+ {thought.details}
+
+ )}
+
+ ))}
+
+ ))}
+
+ );
+};
+
+function getThoughtColor(type: string): string {
+ switch (type.toLowerCase()) {
+ case 'plan':
+ return 'text-blue-400';
+ case 'decision':
+ return 'text-green-400';
+ case 'tool_call':
+ return 'text-yellow-400';
+ case 'tool_result':
+ return 'text-purple-400';
+ case 'think_more':
+ return 'text-pink-400';
+ case 'answer':
+ return 'text-red-400';
+ default:
+ return 'text-gray-400';
+ }
+}
+
+export default ChatContainer;
\ No newline at end of file
diff --git a/dewey/components/ChatTabs.tsx b/dewey/components/ChatTabs.tsx
new file mode 100644
index 0000000..f42fa94
--- /dev/null
+++ b/dewey/components/ChatTabs.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+
+interface ChatTabsProps {
+ chats: Record;
+ currentChatId: string | null;
+ createNewChat: () => void;
+ switchToChat: (chatId: string) => void;
+ closeChat: (chatId: string) => void;
+}
+
+const ChatTabs: React.FC = ({ chats, currentChatId, createNewChat, switchToChat, closeChat }) => {
+ return (
+
+ {Object.keys(chats).map((chatId) => (
+
+
+
+
+ ))}
+
+
+ );
+};
+
+export default ChatTabs;
\ No newline at end of file
diff --git a/dewey/components/Sidebar.tsx b/dewey/components/Sidebar.tsx
new file mode 100644
index 0000000..24d6356
--- /dev/null
+++ b/dewey/components/Sidebar.tsx
@@ -0,0 +1,129 @@
+import React, { useEffect, useRef, useState } from 'react';
+import dynamic from 'next/dynamic';
+
+const Chart = dynamic(() => import('chart.js/auto').then((mod) => mod.Chart), {
+ ssr: false,
+});
+
+interface SidebarProps {
+ socket: any;
+}
+
+const Sidebar: React.FC = ({ socket }) => {
+ const [isCollapsed, setIsCollapsed] = useState(false);
+ const chartRefs = useRef<{ [key: string]: any }>({
+ cpu: null,
+ memory: null,
+ disk: null,
+ gpu: null,
+ gpuMemory: null,
+ });
+
+ useEffect(() => {
+ if (socket) {
+ socket.on('system_resources', (data: any) => {
+ updateCharts(data);
+ });
+ }
+
+ return () => {
+ if (socket) {
+ socket.off('system_resources');
+ }
+ };
+ }, [socket]);
+
+ useEffect(() => {
+ const initCharts = async () => {
+ const ChartJS = await Chart;
+ initializeCharts(ChartJS);
+ };
+ initCharts();
+
+ return () => {
+ Object.values(chartRefs.current).forEach(chart => chart?.destroy());
+ };
+ }, []);
+
+ const initializeCharts = (ChartJS: any) => {
+ const chartConfig = {
+ type: 'line',
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ x: {
+ type: 'time',
+ time: {
+ unit: 'second',
+ },
+ },
+ y: {
+ beginAtZero: true,
+ max: 100,
+ },
+ },
+ animation: false,
+ },
+ data: {
+ datasets: [{
+ data: [],
+ borderColor: 'rgb(75, 192, 192)',
+ tension: 0.1,
+ }],
+ },
+ };
+
+ ['cpu', 'memory', 'disk', 'gpu', 'gpuMemory'].forEach(chartName => {
+ const ctx = document.getElementById(`${chartName}Chart`) as HTMLCanvasElement;
+ if (ctx) {
+ chartRefs.current[chartName] = new ChartJS(ctx, chartConfig);
+ }
+ });
+ };
+
+ const updateCharts = (data: any) => {
+ const now = new Date();
+ Object.entries(data).forEach(([key, value]) => {
+ const chartName = key.replace('_', '').toLowerCase();
+ const chart = chartRefs.current[chartName];
+ if (chart) {
+ chart.data.datasets[0].data.push({x: now, y: value});
+ chart.update('none');
+ }
+ });
+ };
+
+ return (
+
+
+
+
CPU Load
+
+
+
+
Memory Usage
+
+
+
+
Disk I/O
+
+
+
+
GPU Load
+
+
+
+
GPU Memory
+
+
+
+ );
+};
+
+export default Sidebar;
\ No newline at end of file
diff --git a/dewey/components/UserInput.tsx b/dewey/components/UserInput.tsx
new file mode 100644
index 0000000..eaca268
--- /dev/null
+++ b/dewey/components/UserInput.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+
+interface UserInputProps {
+ value: string;
+ onChange: (value: string) => void;
+ onSend: () => void;
+}
+
+const UserInput: React.FC = ({ value, onChange, onSend }) => {
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ onSend();
+ }
+ };
+
+ return (
+
+
+ );
+};
+
+export default UserInput;
\ No newline at end of file
diff --git a/dewey/hooks/useLocalStorage.ts b/dewey/hooks/useLocalStorage.ts
new file mode 100644
index 0000000..3e07434
--- /dev/null
+++ b/dewey/hooks/useLocalStorage.ts
@@ -0,0 +1,23 @@
+import { useState, useEffect } from 'react'
+
+export default function useLocalStorage(key, initialValue) {
+ const [storedValue, setStoredValue] = useState(() => {
+ try {
+ const item = window.localStorage.getItem(key)
+ return item ? JSON.parse(item) : initialValue
+ } catch (error) {
+ console.log(error)
+ return initialValue
+ }
+ })
+
+ useEffect(() => {
+ try {
+ window.localStorage.setItem(key, JSON.stringify(storedValue))
+ } catch (error) {
+ console.log(error)
+ }
+ }, [key, storedValue])
+
+ return [storedValue, setStoredValue]
+}
\ No newline at end of file
diff --git a/dewey/hooks/useSocket.ts b/dewey/hooks/useSocket.ts
new file mode 100644
index 0000000..2db1ae2
--- /dev/null
+++ b/dewey/hooks/useSocket.ts
@@ -0,0 +1,14 @@
+import { useEffect, useState } from 'react'
+import io from 'socket.io-client'
+
+export default function useSocket() {
+ const [socket, setSocket] = useState(null)
+
+ useEffect(() => {
+ const newSocket = io('http://localhost:5001')
+ setSocket(newSocket)
+ return () => newSocket.close()
+ }, [])
+
+ return socket
+}
\ No newline at end of file
diff --git a/dewey/package-lock.json b/dewey/package-lock.json
index cf80198..324f994 100644
--- a/dewey/package-lock.json
+++ b/dewey/package-lock.json
@@ -8,19 +8,24 @@
"name": "dewey",
"version": "0.1.0",
"dependencies": {
- "next": "14.2.13",
- "react": "^18",
- "react-dom": "^18"
+ "autoprefixer": "^10.4.20",
+ "chart.js": "^3.9.1",
+ "chartjs-adapter-moment": "^1.0.1",
+ "marked": "^4.3.0",
+ "next": "^14.2.13",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "socket.io-client": "^4.8.0"
},
"devDependencies": {
- "@types/node": "^20",
- "@types/react": "^18",
- "@types/react-dom": "^18",
+ "@types/node": "^20.16.10",
+ "@types/react": "^18.3.10",
+ "@types/react-dom": "^18.3.0",
"eslint": "^8",
"eslint-config-next": "14.2.13",
- "postcss": "^8",
- "tailwindcss": "^3.4.1",
- "typescript": "^5"
+ "postcss": "^8.4.47",
+ "tailwindcss": "^3.4.13",
+ "typescript": "^5.6.2"
}
},
"node_modules/@alloc/quick-lru": {
@@ -467,6 +472,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+ "license": "MIT"
+ },
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
@@ -1027,6 +1038,43 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/autoprefixer": {
+ "version": "10.4.20",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
+ "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.3",
+ "caniuse-lite": "^1.0.30001646",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -1107,6 +1155,38 @@
"node": ">=8"
}
},
+ "node_modules/browserslist": {
+ "version": "4.24.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz",
+ "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001663",
+ "electron-to-chromium": "^1.5.28",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -1195,6 +1275,22 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chart.js": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
+ "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
+ "license": "MIT"
+ },
+ "node_modules/chartjs-adapter-moment": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
+ "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "chart.js": ">=3.0.0",
+ "moment": "^2.10.2"
+ }
+ },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -1376,7 +1472,6 @@
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1500,6 +1595,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.29",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz",
+ "integrity": "sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==",
+ "license": "ISC"
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -1507,6 +1608,28 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/engine.io-client": {
+ "version": "6.6.1",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
+ "integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.17.1",
+ "xmlhttprequest-ssl": "~2.1.1"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
@@ -1708,6 +1831,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2322,6 +2454,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3368,6 +3513,18 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/marked": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -3425,11 +3582,20 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
"license": "MIT"
},
"node_modules/mz": {
@@ -3547,6 +3713,12 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/node-releases": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+ "license": "MIT"
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -3557,6 +3729,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -3883,7 +4064,6 @@
"version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -4039,7 +4219,6 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/prelude-ls": {
@@ -4451,6 +4630,34 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/socket.io-client": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz",
+ "integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.2",
+ "engine.io-client": "~6.6.1",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+ "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5044,6 +5251,36 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
+ "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -5278,6 +5515,35 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/ws": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xmlhttprequest-ssl": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
+ "integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/yaml": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
diff --git a/dewey/package.json b/dewey/package.json
index f6895d2..d790622 100644
--- a/dewey/package.json
+++ b/dewey/package.json
@@ -9,18 +9,23 @@
"lint": "next lint"
},
"dependencies": {
- "react": "^18",
- "react-dom": "^18",
- "next": "14.2.13"
+ "autoprefixer": "^10.4.20",
+ "chart.js": "^3.9.1",
+ "chartjs-adapter-moment": "^1.0.1",
+ "marked": "^4.3.0",
+ "next": "^14.2.13",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "socket.io-client": "^4.8.0"
},
"devDependencies": {
- "typescript": "^5",
- "@types/node": "^20",
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "postcss": "^8",
- "tailwindcss": "^3.4.1",
+ "@types/node": "^20.16.10",
+ "@types/react": "^18.3.10",
+ "@types/react-dom": "^18.3.0",
"eslint": "^8",
- "eslint-config-next": "14.2.13"
+ "eslint-config-next": "14.2.13",
+ "postcss": "^8.4.47",
+ "tailwindcss": "^3.4.13",
+ "typescript": "^5.6.2"
}
}
diff --git a/main.py b/main.py
index 47ceccf..a0370fb 100644
--- a/main.py
+++ b/main.py
@@ -26,7 +26,7 @@ tool_manager = DefaultToolManager()
@app.route('/')
def index():
- logger.info("Serving index.html")
+ logger.info("Serving index.html")
return send_from_directory('.', 'index.html')
class ChatRequest(BaseModel):
@@ -99,7 +99,7 @@ def answer_question_tools(user_input: str, conversation_history: List[dict], max
emit('conversation_history', {'history': conversation_history})
emit('thought', {'type': 'tool_result', 'content': tool_response})
- reflection_prompt = "Reflect on the tool results. If there were any errors, propose multiple alternative approaches to solve the problem. If successful, consider if the result fully answers the user's query or if additional steps are needed."
+ reflection_prompt = "Reflect on the tool results. If there were any errors, propose multiple alternative approaches to solve the problem. If successful, consider if the result fully answers the user's query or if additional steps are needed. If input is needed, ask the user for clarification with ."
conversation_history.append({
"role": "assistant",
"content": reflection_prompt
@@ -112,9 +112,20 @@ def answer_question_tools(user_input: str, conversation_history: List[dict], max
final_answer = answer_content.group(1).strip()
emit('thought', {'type': 'answer', 'content': final_answer})
return final_answer
+ elif "" in assistant_message['content'].lower():
+ clarification_content = re.search(r'(.*?)', assistant_message['content'], re.DOTALL)
+ if clarification_content:
+ clarification_answer = clarification_content.group(1).strip()
+ emit('thought', {'type': 'clarification', 'content': clarification_answer})
+ return clarification_answer
else:
- emit('thought', {'type': 'decision', 'content': "Think/Plan/Decision/Action\n\n" + assistant_message['content']})
- reflection_prompt = "Your last response didn't provide a final answer. Please reflect on your current understanding of the problem and consider if you need to use any tools or if you can now provide a final answer. If you're ready to give a final answer, put your response in tags "
+ if assistant_message['content'].strip():
+ emit('thought', {'type': 'think_more', 'content': "Think/Plan/Decision/Action\n\n" + assistant_message['content']})
+ reflection_prompt = "Your last response didn't provide a final answer or was incorrectly formatted. Please reflect on your current understanding of the problem and consider if you need to use any tools or if you can now provide a final answer. If you're ready to give a final answer, or need more input from the user, put your response in tags ."
+ else:
+ emit('thought', {'type': 'think_more', 'content': "Previous step failed. Retrying..."})
+ reflection_prompt = "The previous step failed to produce any content. Please retry the previous step, considering alternative approaches or tools that might help solve the problem. If you're ready to give a final answer, or need more input from the user, put your response in tags ."
+
conversation_history.append({"role": "assistant", "content": reflection_prompt})
emit('conversation_history', {'history': conversation_history})
@@ -133,6 +144,7 @@ When addressing a query, follow these steps:
2. Plan: Develop a plan of action, considering whether you need to use any tools or if you can answer directly.
3. Execute: If you need to use a tool, call it as you would a function. If not, proceed with your reasoning.
+ - Analyse the given prompt and decided whether or not it can be answered by a tool. If it can, use the following functions to respond with a JSON for a function call with its proper arguments that best answers the given prompt. Respond in the format \"name\": function name, \"parameters\": dictionary of argument name and its value. Do not use variables.
4. Reflect: After each step or tool use, reflect on the results:
- If successful, consider if the result fully answers the user's query or if additional steps are needed.
@@ -147,12 +159,13 @@ When addressing a query, follow these steps:
6. Conclude: When you believe you have a comprehensive answer to the user's query, provide your final answer.
-Always explain your thought process, including your reasoning for each decision and how you arrived at your conclusions. If you're providing a final answer, put your response in tags .
+Always explain your thought process, including your reasoning for each decision and how you arrived at your conclusions. If you're providing a final answer, or need more input from the user, put your response in tags .
Remember, complex problems often require multiple steps and iterations. Don't hesitate to break down the problem, use tools multiple times, or explore different approaches to arrive at the best solution.
+Before approaching a problem, come up with a few ways you might solve it, and then choose the most promising approach. Repeat this on each iteration.
"""
-PRIMARY_MODEL = "llama3.1:8b"
+PRIMARY_MODEL = "qwen2.5:14b"
UPDATE_INTERVAL = 0.1 # 100ms, configurable
diff --git a/next.config.mjs b/next.config.mjs
new file mode 100644
index 0000000..3644631
--- /dev/null
+++ b/next.config.mjs
@@ -0,0 +1,13 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+ webpack: (config) => {
+ config.externals.push({
+ 'utf-8-validate': 'commonjs utf-8-validate',
+ 'bufferutil': 'commonjs bufferutil',
+ })
+ return config
+ },
+}
+
+export default nextConfig;
\ No newline at end of file
diff --git a/tools.py b/tools.py
index 5693504..a0aba98 100644
--- a/tools.py
+++ b/tools.py
@@ -2,10 +2,10 @@ import duckduckgo_search
import requests
from readability.readability import Document
from markdownify import markdownify as md
-import sys
-import time
-import io
import subprocess
+import time
+import tempfile
+
class Tool:
def __init__(self, name: str, description: str, arguments: dict, returns: str):
self.name = name
@@ -80,33 +80,37 @@ class CalculatorTool(Tool):
super().__init__("calculator", "Perform a calculation", {'type': 'object', 'properties': {'expression': {'type': 'string', 'description': 'The mathematical expression to evaluate, should be a python mathematical expression'}}}, "result:string")
def execute(self, arg: dict) -> str:
- try:
- return str(exec(arg["expression"]))
- except Exception as e:
- return f"Error executing code: {str(e)}"
-
+ p = PythonCodeTool()
+ return p.execute({'code': arg['expression']})
class PythonCodeTool(Tool):
def __init__(self):
- super().__init__("python_code", "Execute python code", {'type': 'object', 'properties': {'code': {'type': 'string', 'description': 'The python code to execute, should be a single line of valid python'}}}, "result:string")
+ super().__init__("python_code", "Execute python code",
+ {'type': 'object', 'properties': {'code': {'type': 'string', 'description': 'The python code to execute, can be multiline'}}},
+ "result:string")
def execute(self, arg: dict) -> str:
try:
- start_time = time.time()
- process = subprocess.Popen(['python', '-c', arg['code']],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True)
- stdout, stderr = process.communicate(timeout=10) # 10 second timeout
- end_time = time.time()
- execution_time = end_time - start_time
-
- result = {
- 'stdout': stdout,
- 'stderr': stderr,
- 'return_value': process.returncode,
- 'execution_time': execution_time
- }
+ with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as temp_file:
+ temp_file.write(arg['code'])
+ temp_file.flush()
+
+ start_time = time.time()
+ process = subprocess.Popen(['python', temp_file.name],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True)
+ stdout, stderr = process.communicate(timeout=10) # 10 second timeout
+ end_time = time.time()
+ execution_time = end_time - start_time
+
+ result = {
+ 'stdout': stdout,
+ 'stderr': stderr,
+ 'return_value': process.returncode,
+ 'execution_time': execution_time
+ }
+
except subprocess.TimeoutExpired:
process.kill()
return "Error: Code execution timed out after 10 seconds"