New frontend and tools
This commit is contained in:
parent
b56619a2e6
commit
9d25dd7d94
@ -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 */
|
||||
|
@ -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 (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
<head>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.1/dist/chartjs-adapter-moment.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@400;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -1,101 +1,44 @@
|
||||
import Image from "next/image";
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import ChatArea from '../components/ChatArea'
|
||||
import Sidebar from '../components/Sidebar'
|
||||
import useSocket from '../hooks/useSocket'
|
||||
import useLocalStorage from '../hooks/useLocalStorage'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="https://nextjs.org/icons/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
const [currentChatId, setCurrentChatId] = useState<string | null>(null)
|
||||
const [chats, setChats] = useLocalStorage('chats', {})
|
||||
const socket = useSocket()
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="https://nextjs.org/icons/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="https://nextjs.org/icons/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="https://nextjs.org/icons/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="https://nextjs.org/icons/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
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 (
|
||||
<div className="flex h-screen">
|
||||
<ChatArea
|
||||
currentChatId={currentChatId}
|
||||
setCurrentChatId={setCurrentChatId}
|
||||
chats={chats}
|
||||
setChats={setChats}
|
||||
createNewChat={createNewChat}
|
||||
socket={socket}
|
||||
/>
|
||||
<Sidebar socket={socket} />
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
133
dewey/components/ChatArea.tsx
Normal file
133
dewey/components/ChatArea.tsx
Normal file
@ -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 (
|
||||
<div className="flex flex-col flex-1">
|
||||
<ChatTabs
|
||||
chats={chats}
|
||||
currentChatId={currentChatId}
|
||||
createNewChat={createNewChat}
|
||||
switchToChat={switchToChat}
|
||||
closeChat={closeChat}
|
||||
/>
|
||||
{currentChatId && (
|
||||
<ChatContainer
|
||||
currentChat={chats[currentChatId]}
|
||||
socket={socket}
|
||||
/>
|
||||
)}
|
||||
<UserInput
|
||||
value={userInput}
|
||||
onChange={setUserInput}
|
||||
onSend={sendMessage}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
85
dewey/components/ChatContainer.tsx
Normal file
85
dewey/components/ChatContainer.tsx
Normal file
@ -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<ChatContainerProps> = ({ currentChat, socket }) => {
|
||||
const chatContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatContainerRef.current) {
|
||||
chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
|
||||
}
|
||||
}, [currentChat]);
|
||||
|
||||
if (!currentChat) return null;
|
||||
|
||||
return (
|
||||
<div ref={chatContainerRef} className="flex-1 overflow-y-auto p-4 bg-gray-900">
|
||||
{currentChat.messages.map((message, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`mb-4 ${
|
||||
message.isUser ? 'text-right text-cyan-300' : 'text-left text-white'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`inline-block p-2 rounded-lg ${
|
||||
message.isUser ? 'bg-cyan-800' : 'bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
{message.isUser ? (
|
||||
message.content
|
||||
) : (
|
||||
<div dangerouslySetInnerHTML={{ __html: marked(message.content) }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{currentChat.thinkingSections.map((section, sectionIndex) => (
|
||||
<div key={sectionIndex} className="mb-4 border-l-2 border-gray-600 pl-4">
|
||||
{section.thoughts.map((thought, thoughtIndex) => (
|
||||
<div key={thoughtIndex} className="mb-2">
|
||||
<div className={`font-bold ${getThoughtColor(thought.type)}`}>
|
||||
{thought.type}:
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{ __html: marked(thought.content) }} />
|
||||
{thought.details && (
|
||||
<pre className="mt-2 p-2 bg-gray-800 rounded">
|
||||
{thought.details}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
48
dewey/components/ChatTabs.tsx
Normal file
48
dewey/components/ChatTabs.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ChatTabsProps {
|
||||
chats: Record<string, any>;
|
||||
currentChatId: string | null;
|
||||
createNewChat: () => void;
|
||||
switchToChat: (chatId: string) => void;
|
||||
closeChat: (chatId: string) => void;
|
||||
}
|
||||
|
||||
const ChatTabs: React.FC<ChatTabsProps> = ({ chats, currentChatId, createNewChat, switchToChat, closeChat }) => {
|
||||
return (
|
||||
<div className="flex bg-gray-800 p-2">
|
||||
{Object.keys(chats).map((chatId) => (
|
||||
<div
|
||||
key={chatId}
|
||||
className={`px-4 py-2 mr-2 rounded-t-lg flex items-center ${
|
||||
chatId === currentChatId ? 'bg-gray-600' : 'bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
onClick={() => switchToChat(chatId)}
|
||||
className="flex-grow text-left"
|
||||
>
|
||||
Chat {chatId}
|
||||
</button>
|
||||
<button
|
||||
className="ml-2 text-red-500 hover:text-red-700"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
closeChat(chatId);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
className="px-4 py-2 bg-green-600 rounded-t-lg"
|
||||
onClick={createNewChat}
|
||||
>
|
||||
+ New Chat
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatTabs;
|
129
dewey/components/Sidebar.tsx
Normal file
129
dewey/components/Sidebar.tsx
Normal file
@ -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<SidebarProps> = ({ 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 (
|
||||
<div className={`w-80 bg-gray-800 p-4 ${isCollapsed ? 'hidden' : ''}`}>
|
||||
<button
|
||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
className="mb-4 px-4 py-2 bg-gray-700 text-white rounded-lg"
|
||||
>
|
||||
{isCollapsed ? 'Show Charts' : 'Hide Charts'}
|
||||
</button>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">CPU Load</h3>
|
||||
<canvas id="cpuChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">Memory Usage</h3>
|
||||
<canvas id="memoryChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">Disk I/O</h3>
|
||||
<canvas id="diskChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">GPU Load</h3>
|
||||
<canvas id="gpuChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">GPU Memory</h3>
|
||||
<canvas id="gpuMemoryChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
37
dewey/components/UserInput.tsx
Normal file
37
dewey/components/UserInput.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
|
||||
interface UserInputProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onSend: () => void;
|
||||
}
|
||||
|
||||
const UserInput: React.FC<UserInputProps> = ({ value, onChange, onSend }) => {
|
||||
const handleKeyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
onSend();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-gray-800">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
className="w-full p-2 bg-gray-700 text-white rounded-lg resize-none"
|
||||
rows={3}
|
||||
placeholder="Type your message here..."
|
||||
/>
|
||||
<button
|
||||
onClick={onSend}
|
||||
className="mt-2 px-4 py-2 bg-blue-600 text-white rounded-lg"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInput;
|
23
dewey/hooks/useLocalStorage.ts
Normal file
23
dewey/hooks/useLocalStorage.ts
Normal file
@ -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]
|
||||
}
|
14
dewey/hooks/useSocket.ts
Normal file
14
dewey/hooks/useSocket.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import io from 'socket.io-client'
|
||||
|
||||
export default function useSocket() {
|
||||
const [socket, setSocket] = useState<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const newSocket = io('http://localhost:5001')
|
||||
setSocket(newSocket)
|
||||
return () => newSocket.close()
|
||||
}, [])
|
||||
|
||||
return socket
|
||||
}
|
292
dewey/package-lock.json
generated
292
dewey/package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
25
main.py
25
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 <answer></answer>."
|
||||
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 "<clarification>" in assistant_message['content'].lower():
|
||||
clarification_content = re.search(r'<clarification>(.*?)</clarification>', 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 <answer></answer>"
|
||||
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 <answer></answer>."
|
||||
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 <answer></answer>."
|
||||
|
||||
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 <answer></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, or need more input from the user, put your response in tags <answer></answer>.
|
||||
|
||||
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
|
||||
|
||||
|
13
next.config.mjs
Normal file
13
next.config.mjs
Normal file
@ -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;
|
52
tools.py
52
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"
|
||||
|
Loading…
Reference in New Issue
Block a user