mirror of
https://github.com/aimingmed/aimingmed-ai.git
synced 2026-02-01 20:23:34 +08:00
update working
This commit is contained in:
parent
896d38e79f
commit
4e5f9f57c2
@ -435,10 +435,58 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||
"question": data_json[0]["content"]
|
||||
}
|
||||
async for chunk in app.astream(inputs):
|
||||
await manager.send_personal_message(
|
||||
json.dumps({"type": "message", "payload": chunk.get("content", str(chunk))}),
|
||||
websocket,
|
||||
)
|
||||
# Determine if chunk is intermediate or final
|
||||
if isinstance(chunk, dict):
|
||||
if len(chunk) == 1:
|
||||
step_name = list(chunk.keys())[0]
|
||||
step_value = chunk[step_name]
|
||||
# Check if this step contains the final answer
|
||||
if isinstance(step_value, dict) and 'generation' in step_value:
|
||||
await manager.send_personal_message(
|
||||
json.dumps({
|
||||
"type": "final",
|
||||
"title": "Answer",
|
||||
"payload": step_value['generation']
|
||||
}),
|
||||
websocket,
|
||||
)
|
||||
else:
|
||||
await manager.send_personal_message(
|
||||
json.dumps({
|
||||
"type": "intermediate",
|
||||
"title": step_name.replace('_', ' ').title(),
|
||||
"payload": str(step_value)
|
||||
}),
|
||||
websocket,
|
||||
)
|
||||
elif 'generation' in chunk:
|
||||
await manager.send_personal_message(
|
||||
json.dumps({
|
||||
"type": "final",
|
||||
"title": "Answer",
|
||||
"payload": chunk['generation']
|
||||
}),
|
||||
websocket,
|
||||
)
|
||||
else:
|
||||
await manager.send_personal_message(
|
||||
json.dumps({
|
||||
"type": "intermediate",
|
||||
"title": "Step",
|
||||
"payload": str(chunk)[:500]
|
||||
}),
|
||||
websocket,
|
||||
)
|
||||
else:
|
||||
# Fallback for non-dict chunks
|
||||
await manager.send_personal_message(
|
||||
json.dumps({
|
||||
"type": "intermediate",
|
||||
"title": "Step",
|
||||
"payload": str(chunk)[:500]
|
||||
}),
|
||||
websocket,
|
||||
)
|
||||
# Send a final 'done' message to signal completion
|
||||
await manager.send_personal_message(
|
||||
json.dumps({"type": "done"}),
|
||||
|
||||
@ -33,6 +33,8 @@ You must not use any information that is not present in the provided context to
|
||||
If you don't know the answer, just say that you don't know.\n
|
||||
Provide the answer in a concise and organized manner. \n
|
||||
|
||||
Reformat the answer in a human-readable markdown format, include underlined or new lines to improve readability if needed.\n
|
||||
|
||||
Question: {question} \n
|
||||
Context: {context} \n
|
||||
Answer:
|
||||
|
||||
@ -12,7 +12,7 @@ ARG ENV_FILE=.env.test
|
||||
COPY ${ENV_FILE} /usr/src/app/.env
|
||||
|
||||
# Copy dependency files and install dependencies
|
||||
RUN npm install && npm i --save-dev @types/jest
|
||||
RUN npm install && npm install --save-dev @types/jest
|
||||
|
||||
EXPOSE 80
|
||||
CMD [ "npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "80" ]
|
||||
1460
app/frontend/package-lock.json
generated
1460
app/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,8 @@
|
||||
"dependencies": {
|
||||
"daisyui": "^5.0.17",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"react-markdown": "^10.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
@ -37,4 +38,4 @@
|
||||
"vite": "^6.2.0",
|
||||
"vitest": "^3.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
const BASE_DOMAIN_NAME_PORT = import.meta.env.REACT_APP_DOMAIN_NAME_PORT || 'localhost:8004';
|
||||
|
||||
@ -8,58 +9,77 @@ interface Message {
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface ChatTurn {
|
||||
question: string;
|
||||
intermediateMessages: { title: string; payload: string }[];
|
||||
finalAnswer: string | null;
|
||||
isLoading: boolean;
|
||||
showIntermediate: boolean;
|
||||
}
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [intermediateMessages, setIntermediateMessages] = useState<{title: string, payload: string}[]>([]);
|
||||
const [finalAnswer, setFinalAnswer] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [showIntermediate, setShowIntermediate] = useState(false);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [socket, setSocket] = useState<WebSocket | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const mounted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
mounted.current = true;
|
||||
const ws = new WebSocket(`ws://${BASE_DOMAIN_NAME_PORT}/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 }];
|
||||
mounted.current = true;
|
||||
const ws = new WebSocket(`ws://${BASE_DOMAIN_NAME_PORT}/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) {
|
||||
// legacy support, treat as final
|
||||
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 }];
|
||||
}
|
||||
});
|
||||
setFinalAnswer(data.payload);
|
||||
} else if (data.type === 'intermediate') {
|
||||
setIntermediateMessages((prev) => [...prev, { title: data.title, payload: data.payload }]);
|
||||
} else if (data.type === 'final') {
|
||||
setFinalAnswer(data.payload);
|
||||
} else if (data.type === 'done') {
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
console.error('Unexpected message format:', data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing message:', error);
|
||||
}
|
||||
});
|
||||
} else if (data.type === 'done') {
|
||||
setIsLoading(false);
|
||||
} 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();
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
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() !== '') {
|
||||
if (newMessage.trim() !== '' && !isLoading) {
|
||||
setIsLoading(true);
|
||||
const message = [{ role: 'user', content: newMessage }];
|
||||
setIntermediateMessages([]);
|
||||
setFinalAnswer(null);
|
||||
setMessages((prevMessages) => [...prevMessages, { sender: 'user', text: newMessage }]);
|
||||
const message = [{ role: 'user', content: newMessage }];
|
||||
socket?.send(JSON.stringify(message));
|
||||
setNewMessage('');
|
||||
}
|
||||
@ -76,6 +96,52 @@ const App: React.FC = () => {
|
||||
{msg.text}
|
||||
</div>
|
||||
))}
|
||||
{/* Status box for intermediate steps */}
|
||||
{intermediateMessages.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="bg-blue-50 border border-blue-300 rounded-lg p-3 shadow-sm flex items-center">
|
||||
{/* Spinner icon */}
|
||||
{isLoading && (
|
||||
<svg className="animate-spin h-5 w-5 text-blue-500 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"></path>
|
||||
</svg>
|
||||
)}
|
||||
<span className="font-semibold text-blue-700 mr-2">Working on:</span>
|
||||
{/* Key steps summary */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{intermediateMessages.map((msg, idx) => (
|
||||
<span key={idx} className="bg-blue-100 text-blue-700 px-2 py-1 rounded text-xs font-medium border border-blue-200">
|
||||
{msg.title}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
className="ml-auto text-xs text-blue-600 underline focus:outline-none"
|
||||
onClick={() => setShowIntermediate((v) => !v)}
|
||||
>
|
||||
{showIntermediate ? 'Hide details' : 'Show details'}
|
||||
</button>
|
||||
</div>
|
||||
{/* Expanded details */}
|
||||
{showIntermediate && (
|
||||
<div className="bg-white border border-blue-200 rounded-b-lg p-3 mt-1 text-xs max-h-64 overflow-y-auto">
|
||||
{intermediateMessages.map((msg, idx) => (
|
||||
<div key={idx} className="mb-3">
|
||||
<div className="font-bold text-blue-700 mb-1">{msg.title}</div>
|
||||
<pre className="whitespace-pre-wrap break-words text-gray-800">{msg.payload}</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Final answer for this question */}
|
||||
{finalAnswer && (
|
||||
<div className="p-4 rounded-lg mb-2 bg-gray-200 text-gray-800 prose max-w-none">
|
||||
<ReactMarkdown>{finalAnswer}</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-300">
|
||||
<div className="flex">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user