Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Human in the loop - frontend support #762

Merged
merged 4 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ async def websocket_endpoint(websocket: WebSocket):
},
}
)
elif data.startswith("human_feedback"):
# Handle human feedback
feedback_data = json.loads(data[14:]) # Remove "human_feedback" prefix
# Process the feedback data as needed
# You might want to send this feedback to the appropriate agent or update the research state
print(f"Received human feedback: {feedback_data}")
# You can add logic here to forward the feedback to the appropriate agent or update the research state
else:
print("Error: not enough parameters provided.")
except WebSocketDisconnect:
Expand Down
27 changes: 19 additions & 8 deletions multi_agents/agents/human.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,24 @@ def __init__(self, websocket=None, stream_output=None, headers=None):
self.headers = headers or {}

async def review_plan(self, research_state: dict):
"""
Review a draft article
:param draft_state:
:return:
"""
print(f"HumanAgent websocket: {self.websocket}")
print(f"HumanAgent stream_output: {self.stream_output}")
layout = research_state.get("sections")
user_feedback = input(f"Any feedback on this plan? {layout}? If not, please reply with 'no'.\n>> ")
if "no" in user_feedback:

user_feedback = None

if self.websocket and self.stream_output:
try:
await self.stream_output("human_feedback", "request", f"Any feedback on this plan? {layout}? If not, please reply with 'no'.", self.websocket)
response = await self.websocket.receive_text()
print(f"Received response: {response}") # Add this line for debugging
user_feedback = response
except Exception as e:
print(f"Error receiving human feedback: {e}")
else:
user_feedback = input(f"Any feedback on this plan? {layout}? If not, please reply with 'no'.\n>> ")

if user_feedback.lower() == "no":
user_feedback = None
return {"human_feedback": user_feedback}

return {"human_feedback": user_feedback}
42 changes: 33 additions & 9 deletions multi_agents/frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import LogMessage from '../components/Task/LogMessage';

import { startLanggraphResearch } from '../components/Langgraph/Langgraph';
import findDifferences from '../helpers/findDifferences';
import HumanFeedback from "@/components/HumanFeedback";

export default function Home() {
const [promptValue, setPromptValue] = useState("");
Expand All @@ -32,6 +33,7 @@ export default function Home() {
const [socket, setSocket] = useState(null);
const [orderedData, setOrderedData] = useState([]);
const heartbeatInterval = useRef(null);
const [showHumanFeedback, setShowHumanFeedback] = useState(false);

useEffect(() => {
if (chatContainerRef.current) {
Expand Down Expand Up @@ -68,16 +70,22 @@ export default function Home() {
newSocket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('websocket data caught in frontend: ', data);
const contentAndType = `${data.content}-${data.type}`;
setOrderedData((prevOrder) => [...prevOrder, { ...data, contentAndType }]);

if (data.type === 'report') {
setAnswer((prev) => prev + data.output);
} else if (data.type === 'path') {
setLoading(false);
newSocket.close();
setSocket(null);

if (data.type === 'human_feedback' && data.content === 'request') {
setShowHumanFeedback(true);
} else {
const contentAndType = `${data.content}-${data.type}`;
setOrderedData((prevOrder) => [...prevOrder, { ...data, contentAndType }]);

if (data.type === 'report') {
setAnswer((prev) => prev + data.output);
} else if (data.type === 'path') {
setLoading(false);
newSocket.close();
setSocket(null);
}
}

};

newSocket.onopen = () => {
Expand All @@ -103,6 +111,15 @@ export default function Home() {
}
};

// Add this function to handle feedback submission
const handleFeedbackSubmit = (feedback: string | null) => {
console.log('user feedback is passed to handleFeedbackSubmit: ', feedback);
if (socket) {
socket.send(JSON.stringify({ type: 'human_feedback', content: feedback }));
}
setShowHumanFeedback(false);
};

const handleDisplayResult = async (newQuestion?: string) => {
newQuestion = newQuestion || promptValue;

Expand Down Expand Up @@ -325,6 +342,13 @@ export default function Home() {
{renderComponentsInOrder()}
</div>

{showHumanFeedback && (
<HumanFeedback
websocket={socket}
onFeedbackSubmit={handleFeedbackSubmit}
/>
)}

<div className="pt-1 sm:pt-2" ref={chatContainerRef}></div>
</div>
<div id="input-area" className="container px-4 lg:px-0">
Expand Down
56 changes: 56 additions & 0 deletions multi_agents/frontend/components/HumanFeedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// /multi_agents/frontend/components/HumanFeedback.tsx

import React, { useState, useEffect } from 'react';

interface HumanFeedbackProps {
websocket: WebSocket | null;
onFeedbackSubmit: (feedback: string | null) => void;
}

const HumanFeedback: React.FC<HumanFeedbackProps> = ({ websocket, onFeedbackSubmit }) => {
const [feedbackRequest, setFeedbackRequest] = useState<string | null>(null);
const [userFeedback, setUserFeedback] = useState<string>('');

useEffect(() => {
if (websocket) {
websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'human_feedback' && data.content === 'request') {
setFeedbackRequest(data.output);
}
};
}
}, [websocket]);

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onFeedbackSubmit(userFeedback === '' ? null : userFeedback);
setFeedbackRequest(null);
setUserFeedback('');
};

if (!feedbackRequest) return null;

return (
<div className="bg-gray-100 p-4 rounded-lg shadow-md">
<h3 className="text-lg font-semibold mb-2">Human Feedback Required</h3>
<p className="mb-4">{feedbackRequest}</p>
<form onSubmit={handleSubmit}>
<textarea
className="w-full p-2 border rounded-md"
value={userFeedback}
onChange={(e) => setUserFeedback(e.target.value)}
placeholder="Enter your feedback here (or leave blank for 'no')"
/>
<button
type="submit"
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
Submit Feedback
</button>
</form>
</div>
);
};

export default HumanFeedback;
Loading