diff --git a/.gitignore b/.gitignore index fc02e99..3dea82b 100644 --- a/.gitignore +++ b/.gitignore @@ -213,4 +213,7 @@ jspm_packages # Optional REPL history .node_repl_history -.next \ No newline at end of file +.next + +config.ini +*.db \ No newline at end of file diff --git a/client.py b/client.py new file mode 100644 index 0000000..4613500 --- /dev/null +++ b/client.py @@ -0,0 +1,138 @@ +import time + +import requests + + +class LLMChatClient: + def __init__(self, base_url, api_key): + self.base_url = base_url.rstrip("/") + self.api_key = api_key + self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"} + + def submit_query(self, message): + """ + Submit a query to the LLM Chat Server. + + Args: + message (str): The message to send to the server. + + Returns: + str: The query ID for the submitted query. + + Raises: + requests.RequestException: If the request fails. + + Example: + client = LLMChatClient('http://localhost:5001', 'your-api-key') + query_id = client.submit_query('What is the capital of France?') + print(f"Query ID: {query_id}") + + cURL equivalent: + curl -X POST http://localhost:5001/api/v1/query \ + -H "Content-Type: application/json" \ + -H "X-API-Key: your-api-key" \ + -d '{"message": "What is the capital of France?"}' + """ + url = f"{self.base_url}/api/v1/query" + data = {"message": message} + response = requests.post(url, json=data, headers=self.headers) + response.raise_for_status() + return response.json()["query_id"] + + def get_query_status(self, query_id): + """ + Get the status of a submitted query. + + Args: + query_id (str): The ID of the query to check. + + Returns: + dict: A dictionary containing the status and conversation history (if completed). + + Raises: + requests.RequestException: If the request fails. + + Example: + client = LLMChatClient('http://localhost:5001', 'your-api-key') + status = client.get_query_status('query-id-here') + print(f"Query status: {status['status']}") + if status['status'] == 'completed': + print(f"Conversation history: {status['conversation_history']}") + + cURL equivalent: + curl -X GET http://localhost:5001/api/v1/query_status/query-id-here \ + -H "X-API-Key: your-api-key" + """ + url = f"{self.base_url}/api/v1/query_status/{query_id}" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return response.json() + + def submit_query_and_wait(self, message, max_wait_time=300, poll_interval=2): + """ + Submit a query and wait for the result. + + Args: + message (str): The message to send to the server. + max_wait_time (int): Maximum time to wait for the result in seconds. + poll_interval (int): Time between status checks in seconds. + + Returns: + dict: The completed conversation history. + + Raises: + requests.RequestException: If the request fails. + TimeoutError: If the query doesn't complete within max_wait_time. + + Example: + client = LLMChatClient('http://localhost:5001', 'your-api-key') + result = client.submit_query_and_wait('What is the capital of France?') + print(f"Conversation history: {result}") + """ + query_id = self.submit_query(message) + start_time = time.time() + + while time.time() - start_time < max_wait_time: + status = self.get_query_status(query_id) + if status["status"] == "completed": + return status["conversation_history"] + time.sleep(poll_interval) + + raise TimeoutError(f"Query did not complete within {max_wait_time} seconds") + + +class LLMChatAdminClient: + def __init__(self, base_url, admin_key): + self.base_url = base_url.rstrip("/") + self.admin_key = admin_key + self.headers = {"X-Admin-Key": admin_key, "Content-Type": "application/json"} + + def generate_api_key(self, username): + """ + Generate a new API key for a user. + + Args: + username (str): The username to generate the API key for. + + Returns: + dict: A dictionary containing the username and generated API key. + + Raises: + requests.RequestException: If the request fails. + + Example: + admin_client = LLMChatAdminClient('http://localhost:5001', 'your-admin-key') + result = admin_client.generate_api_key('new_user') + print(f"Generated API key for {result['username']}: {result['api_key']}") + + cURL equivalent: + curl -X POST http://localhost:5001/admin/generate_key \ + -H "Content-Type: application/json" \ + -H "X-Admin-Key: your-admin-key" \ + -d '{"username": "new_user"}' + """ + url = f"{self.base_url}/admin/generate_key" + data = {"username": username} + response = requests.post(url, json=data, headers=self.headers) + response.raise_for_status() + return response.json() diff --git a/dewey/.eslintrc.json b/dewey/.eslintrc.json deleted file mode 100644 index 3722418..0000000 --- a/dewey/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["next/core-web-vitals", "next/typescript"] -} diff --git a/dewey/.gitignore b/dewey/.gitignore deleted file mode 100644 index fd3dbb5..0000000 --- a/dewey/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/dewey/README.md b/dewey/README.md deleted file mode 100644 index e215bc4..0000000 --- a/dewey/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/dewey/app/favicon.ico b/dewey/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/dewey/app/favicon.ico and /dev/null differ diff --git a/dewey/app/fonts/GeistMonoVF.woff b/dewey/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185..0000000 Binary files a/dewey/app/fonts/GeistMonoVF.woff and /dev/null differ diff --git a/dewey/app/fonts/GeistVF.woff b/dewey/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daa..0000000 Binary files a/dewey/app/fonts/GeistVF.woff and /dev/null differ diff --git a/dewey/app/globals.css b/dewey/app/globals.css deleted file mode 100644 index 8a87c98..0000000 --- a/dewey/app/globals.css +++ /dev/null @@ -1,64 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; -} - -body { - 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; -} - -/* 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 deleted file mode 100644 index be4d3f0..0000000 --- a/dewey/app/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import './globals.css' -import { Inter } from 'next/font/google' - -const inter = Inter({ subsets: ['latin'] }) - -export const metadata = { - title: 'DWS Intelligence', - description: 'AI-powered chat application', -} - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - - - - - - - - - - {children} - - ) -} diff --git a/dewey/app/page.tsx b/dewey/app/page.tsx deleted file mode 100644 index 28c25aa..0000000 --- a/dewey/app/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'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() { - 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 deleted file mode 100644 index 7c3742b..0000000 --- a/dewey/components/ChatArea.tsx +++ /dev/null @@ -1,133 +0,0 @@ -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 deleted file mode 100644 index 9c3b1a9..0000000 --- a/dewey/components/ChatContainer.tsx +++ /dev/null @@ -1,85 +0,0 @@ -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 deleted file mode 100644 index f42fa94..0000000 --- a/dewey/components/ChatTabs.tsx +++ /dev/null @@ -1,48 +0,0 @@ -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 deleted file mode 100644 index 24d6356..0000000 --- a/dewey/components/Sidebar.tsx +++ /dev/null @@ -1,129 +0,0 @@ -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 deleted file mode 100644 index eaca268..0000000 --- a/dewey/components/UserInput.tsx +++ /dev/null @@ -1,37 +0,0 @@ -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 ( -
-