Skip to content

Commit 43dca9c

Browse files
committed
implement chat history
1 parent 70b458f commit 43dca9c

File tree

15 files changed

+436
-64
lines changed

15 files changed

+436
-64
lines changed

apps/client/app/components/ui/Header.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
import React from 'react';
33
import { ConnectButton, useWallet } from '@suiet/wallet-kit';
44
import Image from 'next/image';
5+
56
const Header = () => {
6-
const wallet = useWallet();
7-
console.log(wallet.address, wallet.connected);
7+
const { address, connected } = useWallet();
8+
89
return (
910
<div className="w-[75dvw] grid grid-cols-1 md:flex justify-between">
10-
{/* <p style={{
11-
fontFamily:"Manrope",
12-
}} className="text-lg text-right md:text-left">Atoma's Coin Sage</p> */}
1311
<span className="flex items-center">
14-
<Image src="/coinSageLogo.png" width={50} height={50} alt="atomasage logo" />
12+
<Image
13+
src="/coinSageLogo.png"
14+
width={50}
15+
height={50}
16+
alt="atomasage logo"
17+
priority
18+
/>
1519
<p
1620
style={{
1721
fontFamily: 'fantasy'
@@ -22,8 +26,15 @@ const Header = () => {
2226
</p>
2327
</span>
2428

25-
<div className="w-10 md:block z-20 hidden ">
26-
<ConnectButton className="" label="Connect Wallet" />
29+
<div className="flex items-center gap-4">
30+
{connected && (
31+
<span className="text-sm text-gray-600">
32+
{address?.slice(0, 6)}...{address?.slice(-4)}
33+
</span>
34+
)}
35+
<div className="w-40 md:block z-20">
36+
<ConnectButton label="Connect Wallet" />
37+
</div>
2738
</div>
2839
</div>
2940
);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client';
2+
import { useWallet } from '@suiet/wallet-kit';
3+
4+
const WalletStatus = () => {
5+
const { connected } = useWallet();
6+
7+
if (!connected) {
8+
return (
9+
<div className="text-center p-4 my-4 bg-yellow-50 border border-yellow-200 rounded-lg text-yellow-700">
10+
Please connect your wallet to start chatting and save your conversation history
11+
</div>
12+
);
13+
}
14+
15+
return null;
16+
};
17+
18+
export default WalletStatus;

apps/client/app/globals.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,11 @@ button {
4444
.logo.react:hover {
4545
filter: drop-shadow(0 0 2em #61dafbaa);
4646
} */
47+
48+
.sui-wallet-kit-button {
49+
@apply px-4 py-2 rounded-lg bg-blue-600 text-white hover:bg-blue-700 transition-colors !important;
50+
}
51+
52+
.sui-wallet-kit-button span {
53+
@apply text-sm font-medium !important;
54+
}

apps/client/app/page.tsx

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client';
2-
import React, { useState } from 'react';
2+
import React, { useState, useEffect } from 'react';
33
import api from './lib/api';
44
import { useWallet } from '@suiet/wallet-kit';
55
import JSONFormatter from './utils/JSONFormatter';
@@ -15,32 +15,42 @@ export default function Home() {
1515
>([]);
1616
const [inputValue, setInputValue] = useState('');
1717
const [isThinking, setIsThinking] = useState(false);
18-
const { address } = useWallet();
18+
const { address, connected } = useWallet();
19+
20+
// Load chat history when wallet connects
21+
useEffect(() => {
22+
if (address && connected) {
23+
loadChatHistory();
24+
// Send initial wallet connection message
25+
handleSend(`Connected wallet: ${address}`);
26+
}
27+
}, [address, connected]);
28+
29+
const loadChatHistory = async () => {
30+
try {
31+
const response = await api.get(`/query/history/${address}`);
32+
setMessages(response.data);
33+
} catch (error) {
34+
console.error('Error loading chat history:', error);
35+
}
36+
};
1937

2038
const handleSend = async (message?: string) => {
2139
const userMessage = message || inputValue.trim();
2240

2341
if (userMessage) {
2442
setMessages((prev) => [...prev, { text: userMessage, sender: 'user' }]);
25-
setIsThinking(true);
2643
setInputValue('');
44+
setIsThinking(true);
2745

2846
try {
29-
let modifiedMessage = userMessage;
30-
31-
const containsKeywords = keywords.some((keyword) =>
32-
userMessage.toLowerCase().includes(keyword)
33-
);
34-
35-
if (containsKeywords) {
36-
modifiedMessage = `${userMessage}. My wallet address is ${address}.`;
37-
}
47+
// Always send the wallet address with the query
48+
const response = await api.post('/query', {
49+
query: userMessage,
50+
walletAddress: address
51+
});
3852

39-
console.log(modifiedMessage, 'modified');
40-
const response = await api.post('/query', { query: modifiedMessage });
41-
console.log(response);
4253
const res = response.data[0];
43-
console.log(res);
4454
let llmResponse = '';
4555

4656
if (typeof res.response === 'string') {

apps/web/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"@atoma-agents/sui-agent": "workspace:*",
1616
"cors": "^2.8.5",
1717
"dotenv": "^16.4.7",
18-
"express": "^4.21.2"
18+
"express": "^4.21.2",
19+
"mongoose": "^8.10.0"
1920
},
2021
"devDependencies": {
2122
"@types/cors": "^2.8.17",

apps/web/src/app.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import express, { Application } from 'express';
22
import cors from 'cors';
33
import v1routes from './routes/v1';
4+
import { connectDB } from './utils/db';
5+
import queryRouter from './routes/v1/query';
46

57
/**
68
* Express application instance.
@@ -27,6 +29,9 @@ app.use(cors()); // Enable CORS for all routes
2729
*/
2830
app.use(v1routes);
2931

32+
// Connect to MongoDB
33+
connectDB().catch(console.error);
34+
3035
/**
3136
* @exports app
3237
* @type {Application}

apps/web/src/models/ChatHistory.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import mongoose, { Schema, Document } from 'mongoose';
2+
3+
export interface IMessage {
4+
text: string;
5+
sender: 'user' | 'llm';
6+
isHTML?: boolean;
7+
timestamp: Date;
8+
}
9+
10+
export interface IChatHistory extends Document {
11+
walletAddress: string;
12+
messages: IMessage[];
13+
createdAt: Date;
14+
updatedAt: Date;
15+
}
16+
17+
const ChatHistorySchema = new Schema(
18+
{
19+
walletAddress: {
20+
type: String,
21+
required: true,
22+
index: true
23+
},
24+
messages: [
25+
{
26+
text: { type: String, required: true },
27+
sender: { type: String, enum: ['user', 'llm'], required: true },
28+
isHTML: { type: Boolean, default: false },
29+
timestamp: { type: Date, default: Date.now }
30+
}
31+
]
32+
},
33+
{
34+
timestamps: true
35+
}
36+
);
37+
38+
export default mongoose.model<IChatHistory>('ChatHistory', ChatHistorySchema);

apps/web/src/routes/v1/query.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Router } from 'express';
22
import { Request, Response } from 'express';
33
import { config } from '../../config';
44
import Agent from '@atoma-agents/sui-agent/src/agents/SuiAgent';
5+
import ChatHistory from '../../models/ChatHistory';
6+
57
const suiAgent = new Agent(config.atomaSdkBearerAuth);
68
const queryRouter: Router = Router();
79

@@ -13,15 +15,53 @@ queryRouter.get('/health', (req: Request, res: Response) => {
1315
// Query endpoint
1416
const handleQuery = async (req: Request, res: Response): Promise<void> => {
1517
try {
16-
const { query } = req.body;
18+
const { query, walletAddress } = req.body;
19+
1720
if (!query) {
1821
res.status(400).json({
1922
error: 'Missing query in request body'
2023
});
2124
return;
2225
}
2326

27+
// Get agent response first
2428
const result = await suiAgent.SuperVisorAgent(query);
29+
30+
// Only try to save chat history if walletAddress is provided
31+
if (walletAddress) {
32+
try {
33+
// Get or create chat history for this wallet
34+
let chatHistory = await ChatHistory.findOne({ walletAddress });
35+
if (!chatHistory) {
36+
chatHistory = new ChatHistory({ walletAddress, messages: [] });
37+
}
38+
39+
// Add user message to history
40+
chatHistory.messages.push({
41+
text: query,
42+
sender: 'user',
43+
timestamp: new Date()
44+
});
45+
46+
// Add agent response to history
47+
chatHistory.messages.push({
48+
text:
49+
typeof result[0].response === 'string'
50+
? result[0].response
51+
: JSON.stringify(result[0].response),
52+
sender: 'llm',
53+
isHTML: true,
54+
timestamp: new Date()
55+
});
56+
57+
// Save chat history
58+
await chatHistory.save();
59+
} catch (chatError) {
60+
console.warn('Error saving chat history:', chatError);
61+
// Continue with the response even if chat history fails
62+
}
63+
}
64+
2565
res.status(200).json(result);
2666
} catch (error) {
2767
console.error('Error handling query:', error);
@@ -31,6 +71,20 @@ const handleQuery = async (req: Request, res: Response): Promise<void> => {
3171
}
3272
};
3373

74+
// Get chat history endpoint
75+
queryRouter.get('/history/:walletAddress', async (req: Request, res: Response) => {
76+
try {
77+
const { walletAddress } = req.params;
78+
const chatHistory = await ChatHistory.findOne({ walletAddress });
79+
res.status(200).json(chatHistory?.messages || []);
80+
} catch (error) {
81+
console.error('Error fetching chat history:', error);
82+
res.status(500).json({
83+
error: 'Internal server error'
84+
});
85+
}
86+
});
87+
3488
// Handle unsupported methods
3589
const handleUnsupportedMethod = (req: Request, res: Response): void => {
3690
res.status(405).json({

apps/web/src/server.ts

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,31 @@
1-
import http from 'http';
21
import app from './app';
3-
import { config } from './config';
2+
import { connectDB, disconnectDB } from './utils/db';
43

5-
/**
6-
* Main application class that initializes and starts the server
7-
*/
8-
class Main {
9-
private port: string | number;
4+
const port = process.env.PORT || 2512;
105

11-
/**
12-
* Initialize the application with port
13-
* @param port - Port number for the server to listen on
14-
*/
15-
constructor(port: string | number) {
16-
this.port = port;
17-
}
6+
async function startServer() {
7+
try {
8+
await connectDB();
189

19-
/**
20-
* Start the HTTP server
21-
*/
22-
async start() {
23-
const server = http.createServer(app);
10+
const server = app.listen(port, () => {
11+
console.log(`Server is running on port ${port}`);
12+
});
2413

25-
// Graceful shutdown handler
26-
const shutdown = () => {
27-
console.log('Shutting down gracefully...');
28-
server.close(() => {
29-
console.log('Server closed');
14+
// Handle graceful shutdown
15+
const shutdown = async () => {
16+
console.log('Shutting down server...');
17+
server.close(async () => {
18+
await disconnectDB();
3019
process.exit(0);
3120
});
3221
};
3322

34-
// Handle shutdown signals
3523
process.on('SIGTERM', shutdown);
3624
process.on('SIGINT', shutdown);
37-
38-
server.listen(this.port, () => {
39-
console.log(`Server is listening on port ${this.port}`);
40-
});
25+
} catch (error) {
26+
console.error('Failed to start server:', error);
27+
process.exit(1);
4128
}
4229
}
4330

44-
// Create and start the application
45-
const main = new Main(config.port);
46-
main.start().catch((error) => {
47-
console.error('Failed to start server:', error);
48-
process.exit(1);
49-
});
31+
startServer();

apps/web/src/types/global.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import mongoose from 'mongoose';
2+
3+
declare global {
4+
var _mongooseCache: {
5+
conn: typeof mongoose | null;
6+
promise: Promise<typeof mongoose> | null;
7+
} | undefined;
8+
}
9+
10+
// Augment the NodeJS namespace to include our custom property
11+
declare module 'node:process' {
12+
global: typeof globalThis & {
13+
_mongooseCache?: {
14+
conn: typeof mongoose | null;
15+
promise: Promise<typeof mongoose> | null;
16+
};
17+
};
18+
}
19+
20+
export {};

0 commit comments

Comments
 (0)