diff --git a/app/frontend/src/App.test.tsx b/app/frontend/src/App.test.tsx
new file mode 100644
index 0000000..310a534
--- /dev/null
+++ b/app/frontend/src/App.test.tsx
@@ -0,0 +1,21 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import App from './App';
+import { vi } from 'vitest';
+
+it('renders initial state', () => {
+ render();
+ expect(screen.getByText('Simple Chatbot')).toBeInTheDocument();
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /send/i })).toBeInTheDocument();
+});
+
+// it('sends a message', () => {
+// const mockSend = vi.fn();
+// const mockSocket = { send: mockSend };
+// render();
+// const inputElement = screen.getByRole('textbox');
+// const buttonElement = screen.getByRole('button', { name: /send/i });
+// fireEvent.change(inputElement, { target: { value: 'Hello' } });
+// fireEvent.keyDown(inputElement, { key: 'Enter', code: 'Enter' }); // Simulate Enter key press
+// expect(mockSend).toHaveBeenCalled();
+// });
\ No newline at end of file
diff --git a/app/frontend/src/App.tsx b/app/frontend/src/App.tsx
index 48f3fa8..0a9f2cf 100644
--- a/app/frontend/src/App.tsx
+++ b/app/frontend/src/App.tsx
@@ -1,34 +1,90 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from '/vite.svg'
+import React, { useState, useEffect, useRef } from 'react';
-function App() {
- const [count, setCount] = useState(0)
-
- return (
- <>
-
- Vite + React
-
-
-
- Edit src/App.tsx and save to test HMR
-
-
-
- Click on the Vite and React logos to learn more
-
- >
- )
+interface Message {
+ sender: 'user' | 'bot';
+ text: string;
}
-export default App
+const App: React.FC = () => {
+ const [messages, setMessages] = useState([]);
+ const [newMessage, setNewMessage] = useState('');
+ const [socket, setSocket] = useState(null);
+ const mounted = useRef(false);
+
+ useEffect(() => {
+ mounted.current = true;
+ const ws = new WebSocket('ws://localhost:8000/ws');
+ setSocket(ws);
+ ws.onopen = () => {
+ console.log('WebSocket connection opened');
+ };
+ ws.onmessage = (event) => {
+ try {
+ const data = JSON.parse(event.data);
+ if (data.type === 'message' && data.payload && mounted.current) {
+ setMessages((prevMessages) => {
+ const lastMessage = prevMessages[prevMessages.length - 1];
+ if (lastMessage && lastMessage.sender === 'bot') {
+ return [...prevMessages.slice(0, -1), { ...lastMessage, text: lastMessage.text + data.payload }];
+ } else {
+ return [...prevMessages, { sender: 'bot', text: data.payload }];
+ }
+ });
+ } else {
+ console.error('Unexpected message format:', data);
+ }
+ } catch (error) {
+ console.error('Error parsing message:', error);
+ }
+ };
+ ws.onclose = () => {
+ console.log('WebSocket connection closed');
+ };
+ ws.onerror = (error) => {
+ console.error('WebSocket error:', error);
+ };
+ return () => {
+ mounted.current = false;
+ ws.close();
+ };
+ }, []);
+
+ const sendMessage = () => {
+ if (newMessage.trim() !== '') {
+ const message = [{ role: 'user', content: newMessage }];
+ setMessages((prevMessages) => [...prevMessages, { sender: 'user', text: newMessage }]);
+ socket?.send(JSON.stringify(message));
+ setNewMessage('');
+ }
+ };
+
+ return (
+
+
+
Simple Chatbot
+
+
+ {messages.map((msg, index) => (
+
+ {msg.text}
+
+ ))}
+
+
+
+ setNewMessage(e.target.value)}
+ className="flex-grow p-2 border border-gray-300 rounded-lg mr-2"
+ />
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/app/llmops/config.yaml b/app/llmops/config.yaml
index eaaf910..af642d2 100644
--- a/app/llmops/config.yaml
+++ b/app/llmops/config.yaml
@@ -15,7 +15,7 @@ rag:
testing:
query: "如何治疗乳腺癌?"
evaluation:
- evaluation_dataset_csv_path: "../../../../data/qa_dataset_20250401b.csv"
+ evaluation_dataset_csv_path: "../../../../data/qa_dataset_20250409_onlyBreast.csv"
evaluation_dataset_column_question: question
evaluation_dataset_column_answer: answer
ls_chat_model_provider: