Skip to content

Commit

Permalink
initial chain agents frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
jfrank-summit committed Nov 1, 2024
1 parent 18d378c commit 1b75631
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 0 deletions.
1 change: 1 addition & 0 deletions auto-chain-agent/frontend/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_URL=http://localhost:3000
17 changes: 17 additions & 0 deletions auto-chain-agent/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<title>Autonomys Network - Chain Agent</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
37 changes: 37 additions & 0 deletions auto-chain-agent/frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@auto-chain/frontend",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"framer-motion": "11.11.11",
"axios": "^1.6.7",
"date-fns": "4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^9.0.1"
},
"devDependencies": {
"@types/node": "22.8.6",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "8.12.2",
"@typescript-eslint/parser": "8.12.2",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "9.14.0",
"eslint-plugin-react-hooks": "5.0.0",
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.1.0"
}
}
Binary file added auto-chain-agent/frontend/public/favicon.ico
Binary file not shown.
93 changes: 93 additions & 0 deletions auto-chain-agent/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState, useRef, useEffect } from 'react';
import { Box, Container, Heading, VStack } from '@chakra-ui/react';
import ChatInput from './components/ChatInput';
import MessageList from './components/MessageList';
import { Message } from './types';
import { sendMessage } from './api';

function App() {
const [messages, setMessages] = useState<Message[]>([]);
const [loading, setLoading] = useState(false);
const [currentThreadId, setCurrentThreadId] = useState<string | null>(null);
const messagesEndRef = useRef<HTMLDivElement>(null);

const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};

useEffect(() => {
scrollToBottom();
}, [messages]);

const handleSendMessage = async (content: string) => {
if (!content.trim()) return;

const userMessage: Message = {
role: 'user',
content,
timestamp: new Date(),
};
setMessages(prev => [...prev, userMessage]);
setLoading(true);

try {
const response = await sendMessage(content, currentThreadId);
setCurrentThreadId(response.threadId);

const assistantMessage: Message = {
role: 'assistant',
content: response.response,
timestamp: new Date(),
toolCalls: response.toolCalls
};

setMessages(prev => [...prev, assistantMessage]);
} catch (error) {
console.error('Error sending message:', error);
const errorMessage: Message = {
role: 'error',
content: 'Sorry, there was an error processing your request.',
timestamp: new Date(),
};
setMessages(prev => [...prev, errorMessage]);
} finally {
setLoading(false);
}
};

return (
<Box minH="100vh" bg="gray.50">
<Box bg="white" py={4} shadow="sm">
<Container maxW="container.xl">
<Heading size="lg" color="gray.700">Autonomys Network - Chain Agent</Heading>
</Container>
</Box>

<Container maxW="container.xl" py={8}>
<Box
bg="white"
borderRadius="lg"
shadow="base"
height="calc(100vh - 180px)"
overflow="hidden"
>
<VStack h="full" spacing={0}>
<Box flex="1" w="full" overflowY="auto" p={4}>
<MessageList messages={messages} />
<div ref={messagesEndRef} />
</Box>

<Box w="full" p={4} borderTop="1px" borderColor="gray.100">
<ChatInput
onSendMessage={handleSendMessage}
disabled={loading}
/>
</Box>
</VStack>
</Box>
</Container>
</Box>
);
}

export default App;
27 changes: 27 additions & 0 deletions auto-chain-agent/frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference types="vite/client" />
import axios from 'axios';

const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';

export const sendMessage = async (message: string, threadId?: string | null) => {
try {
const response = await axios.post(`${API_URL}/chainagent`, {
message,
threadId
});
return response.data;
} catch (error) {
console.error('API Error:', error);
throw error;
}
};

export const getThreadState = async (threadId: string) => {
try {
const response = await axios.get(`${API_URL}/chainagent/${threadId}/state`);
return response.data;
} catch (error) {
console.error('API Error:', error);
throw error;
}
};
67 changes: 67 additions & 0 deletions auto-chain-agent/frontend/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useState, KeyboardEvent } from 'react';
import {
Button,
Textarea,
HStack,
useColorModeValue
} from '@chakra-ui/react';

interface ChatInputProps {
onSendMessage: (message: string) => void;
disabled?: boolean;
}

function ChatInput({ onSendMessage, disabled }: ChatInputProps) {
const [message, setMessage] = useState('');

const handleSubmit = () => {
if (message.trim() && !disabled) {
onSendMessage(message);
setMessage('');
}
};

const handleKeyPress = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit();
}
};

const buttonBg = useColorModeValue('blue.500', 'blue.300');
const buttonHoverBg = useColorModeValue('blue.600', 'blue.400');

return (
<HStack spacing={4} align="end">
<Textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type your message..."
disabled={disabled}
resize="none"
rows={3}
focusBorderColor="blue.500"
/>
<Button
onClick={handleSubmit}
isDisabled={disabled || !message.trim()}
bg={buttonBg}
color="white"
px={8}
h={12}
_hover={{
bg: buttonHoverBg
}}
_disabled={{
bg: 'gray.300',
cursor: 'not-allowed'
}}
>
Send
</Button>
</HStack>
);
}

export default ChatInput;
72 changes: 72 additions & 0 deletions auto-chain-agent/frontend/src/components/MessageList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { format } from 'date-fns';
import ReactMarkdown from 'react-markdown';
import { Message } from '../types';
import {
VStack,
Box,
Text,
useColorModeValue
} from '@chakra-ui/react';

interface MessageListProps {
messages: Message[];
}

function MessageList({ messages }: MessageListProps) {
const userBg = useColorModeValue('blue.500', 'blue.400');
const assistantBg = useColorModeValue('gray.100', 'gray.700');
const errorBg = useColorModeValue('red.100', 'red.900');

return (
<VStack spacing={4} align="stretch">
{messages.map((message, index) => (
<Box
key={index}
display="flex"
flexDirection="column"
alignItems={message.role === 'user' ? 'flex-end' : 'flex-start'}
>
<Box
maxW="80%"
p={4}
borderRadius="lg"
bg={
message.role === 'user'
? userBg
: message.role === 'error'
? errorBg
: assistantBg
}
color={message.role === 'user' ? 'white' : 'inherit'}
>
<Box
className="markdown-content"
sx={{
'& ul': {
listStylePosition: 'inside',
paddingLeft: '0',
marginY: '0.5em'
},
'& li': {
marginY: '0.25em'
},
'& p': {
marginY: '0.5em'
}
}}
>
<ReactMarkdown>
{message.content}
</ReactMarkdown>
</Box>
</Box>
<Text fontSize="xs" color="gray.500" mt={1}>
{format(message.timestamp, 'HH:mm')}
</Text>
</Box>
))}
</VStack>
);
}

export default MessageList;
26 changes: 26 additions & 0 deletions auto-chain-agent/frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
import App from './App';

const theme = extendTheme({
styles: {
global: {
body: {
bg: 'gray.50',
},
},
},
config: {
initialColorMode: 'light',
useSystemColorMode: false,
},
});

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ChakraProvider theme={theme}>
<App />
</ChakraProvider>
</React.StrictMode>,
);
14 changes: 14 additions & 0 deletions auto-chain-agent/frontend/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface Message {
role: 'user' | 'assistant' | 'error';
content: string;
timestamp: Date;
toolCalls?: Array<{
id: string;
type: string;
function: {
name: string;
arguments: string;
};
result?: string;
}>;
}
Loading

0 comments on commit 1b75631

Please sign in to comment.