kamaleswar Mohanta commited on
Commit
9949bd0
·
1 Parent(s): 0e22462

BlogGenerationNode feedback collection to improve JSON parsing and streamline feedback handling

Browse files
src/langgraphagenticai/graph/__pycache__/graph_builder.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/graph/__pycache__/graph_builder.cpython-312.pyc and b/src/langgraphagenticai/graph/__pycache__/graph_builder.cpython-312.pyc differ
 
src/langgraphagenticai/graph/graph_builder.py CHANGED
@@ -163,7 +163,7 @@ class GraphBuilder:
163
  graph_builder.add_edge("revision_generator", "synthesizer") # Loop back to synthesize revised sections
164
 
165
  # Compile with interrupts at review nodes
166
- return graph_builder.compile(interrupt_before=["feedback_collector"], checkpointer=self.memory)
167
  except Exception as e:
168
  logger.error(f"Error building blog generation graph: {e}")
169
  return None
 
163
  graph_builder.add_edge("revision_generator", "synthesizer") # Loop back to synthesize revised sections
164
 
165
  # Compile with interrupts at review nodes
166
+ return graph_builder.compile(interrupt_after=["feedback_collector"], checkpointer=self.memory)
167
  except Exception as e:
168
  logger.error(f"Error building blog generation graph: {e}")
169
  return None
src/langgraphagenticai/nodes/__pycache__/blog_generation_node.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/nodes/__pycache__/blog_generation_node.cpython-312.pyc and b/src/langgraphagenticai/nodes/__pycache__/blog_generation_node.cpython-312.pyc differ
 
src/langgraphagenticai/nodes/blog_generation_node.py CHANGED
@@ -1,27 +1,8 @@
1
- # # src/langgraphagenticai/nodes/blog_generation_node.py
2
- # from src.langgraphagenticai.state.state import State, Section
3
- # from pydantic import BaseModel, Field
4
- # from typing import List, Dict, Optional
5
- # from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
6
- # from langchain_core.prompts import ChatPromptTemplate
7
- # from src.langgraphagenticai.tools.search_tool import get_tools
8
- # import logging
9
- # import streamlit as st
10
- # import json
11
-
12
- # # Configure logging
13
- # logging.basicConfig(level=logging.INFO)
14
- # logger = logging.getLogger(__name__)
15
-
16
-
17
-
18
  import logging
19
  from langgraph.graph import StateGraph, START, END
20
  from langgraph.constants import Send
21
  from src.langgraphagenticai.state.state import State, WorkerState, Sections, Section # Import from state.py
22
  from langchain_core.messages import SystemMessage, HumanMessage
23
- from IPython.display import Markdown, display
24
- from PIL import Image
25
  import streamlit as st
26
  import json
27
 
@@ -95,30 +76,31 @@ class BlogGenerationNode:
95
  return {"final_report": final_report}
96
 
97
  def feedback_collector(self, state: State) -> dict:
98
- """Collect human feedback on the draft using Streamlit interface."""
99
  logger.info("Executing feedback_collector")
100
 
101
- # Get feedback from Streamlit session state
102
- feedback = st.session_state.get("draft_feedback", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- # Check if feedback exists
105
- if feedback:
106
- logger.info(f"Collected feedback: {feedback}")
107
- # Set draft_approved in state
108
- state["draft_approved"] = feedback == "approved"
109
- return {
110
- "feedback": feedback,
111
- "draft_approved": state["draft_approved"]
112
- }
113
- else:
114
- logger.info("No feedback provided yet")
115
- # Set draft_approved in state
116
- state["draft_approved"] = False
117
- return {
118
- "feedback": "",
119
- "draft_approved": state["draft_approved"]
120
- }
121
-
122
  def revision_generator(self,state: State) -> dict:
123
  """Revise the report based on human feedback."""
124
  if state["draft_approved"]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import logging
2
  from langgraph.graph import StateGraph, START, END
3
  from langgraph.constants import Send
4
  from src.langgraphagenticai.state.state import State, WorkerState, Sections, Section # Import from state.py
5
  from langchain_core.messages import SystemMessage, HumanMessage
 
 
6
  import streamlit as st
7
  import json
8
 
 
76
  return {"final_report": final_report}
77
 
78
  def feedback_collector(self, state: State) -> dict:
79
+ """Collect human feedback on the draft."""
80
  logger.info("Executing feedback_collector")
81
 
82
+ # Check if we have a new human message with feedback
83
+ if state["messages"] and isinstance(state["messages"][-1], HumanMessage):
84
+ try:
85
+ # Parse the feedback from the human message
86
+ feedback_data = json.loads(state["messages"][-1].content)
87
+ is_approved = feedback_data.get("approved", False)
88
+ comments = feedback_data.get("comments", "")
89
+
90
+ logger.info(f"Parsed feedback: approved={is_approved}, comments={comments}")
91
+
92
+ return {
93
+ "feedback": comments,
94
+ "draft_approved": is_approved,
95
+ "blog_content": state.get("final_report", "") # Keep the content in state
96
+ }
97
+ except json.JSONDecodeError:
98
+ # Not a valid JSON feedback
99
+ logger.warning("Invalid feedback format")
100
+ return {"feedback": "", "draft_approved": False}
101
 
102
+ # No feedback yet
103
+ return {"feedback": "", "draft_approved": False}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  def revision_generator(self,state: State) -> dict:
105
  """Revise the report based on human feedback."""
106
  if state["draft_approved"]:
src/langgraphagenticai/ui/streamlitui/__pycache__/display_result.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/ui/streamlitui/__pycache__/display_result.cpython-312.pyc and b/src/langgraphagenticai/ui/streamlitui/__pycache__/display_result.cpython-312.pyc differ
 
src/langgraphagenticai/ui/streamlitui/display_result.py CHANGED
@@ -59,7 +59,7 @@ class DisplayResultStreamlit:
59
  if not st.session_state.waiting_for_feedback and st.session_state.graph_state:
60
  graph_state = st.session_state.graph_state
61
  if graph_state.next and graph_state.next[0] == "feedback_collector":
62
- logger.info("Setting waiting_for_feedback based on graph state")
63
  st.session_state.waiting_for_feedback = True
64
 
65
  if st.session_state.waiting_for_feedback:
@@ -101,51 +101,62 @@ class DisplayResultStreamlit:
101
 
102
  def _process_feedback(self):
103
  latest_state = st.session_state.graph_state.values if st.session_state.graph_state else {}
 
 
 
 
104
 
105
  # Display the content if it hasn't been displayed yet
106
- if "blog_content" in latest_state and not st.session_state.get("content_displayed", False):
107
  with st.chat_message("assistant"):
108
  st.markdown("### Generated Blog Content")
109
- st.markdown(self._format_blog_content(latest_state["blog_content"]))
110
  st.session_state.content_displayed = True
 
111
 
112
  # Check if we're at the feedback collection node
113
  current_node = st.session_state.graph_state.next[0] if st.session_state.graph_state.next else None
114
  logger.info(f"Current node in _process_feedback: {current_node}")
115
 
116
  if current_node == "feedback_collector":
117
- st.write("### Review the generated content:")
118
- col1, col2 = st.columns(2)
119
-
120
- with col1:
121
- if st.button("Approve", key="content_approve"):
122
- feedback = {
123
- "approved": True,
124
- "comments": "Content approved."
125
- }
126
- self._submit_feedback(feedback)
127
-
128
- with col2:
129
- with st.expander("Request Revisions"):
130
- comments = st.text_area("Provide revision comments:",
131
- placeholder="Please explain what changes you would like to see.")
132
- if st.button("Submit Revisions"):
133
- if not comments:
134
- st.error("Please provide revision comments.")
135
- else:
136
- feedback = {
137
- "approved": False,
138
- "comments": comments
139
- }
140
- self._submit_feedback(feedback)
 
 
 
 
141
 
142
  def _submit_feedback(self, feedback):
143
  """Submit feedback to the graph and continue processing."""
144
  try:
145
- # Convert feedback to the expected ReviewFeedback format
146
  feedback_json = json.dumps(feedback)
147
  st.session_state.waiting_for_feedback = False
148
  st.session_state.content_displayed = False
 
 
149
  # Continue processing with the feedback
150
  self._process_graph_stream(HumanMessage(content=feedback_json))
151
  logger.info(f"Feedback submitted: {feedback}")
@@ -166,50 +177,54 @@ class DisplayResultStreamlit:
166
  try:
167
  input_data = {"messages": [input_message]} if input_message else None
168
  for event in (self.graph.stream(input_data, self.config) if input_data else self.graph.stream(None, self.config)):
169
- logger.info(f"Graph event: {event}")
 
 
170
  for node, state in event.items():
 
 
 
 
 
 
 
 
 
171
  if "messages" in state and state["messages"]:
172
  with st.chat_message("assistant"):
173
- self._display_result(state)
174
- self.session_history.add_ai_message(state["messages"][-1].content)
 
175
 
176
  graph_state = self.graph.get_state(self.config)
177
- logger.info(f"Graph state next: {graph_state.next}")
178
 
179
  if graph_state.next and graph_state.next[0] == "feedback_collector":
180
  st.session_state.waiting_for_feedback = True
181
  st.session_state.graph_state = graph_state
182
- logger.info("Paused for feedback collection")
183
  st.rerun() # Force UI update to ensure feedback buttons appear
184
  break
185
- elif not graph_state.next and self.usecase == "Blog Generation":
186
- st.session_state.blog_requirements_collected = False
187
- st.session_state.content_displayed = False # Reset for new blog
188
- with st.chat_message("assistant"):
189
- st.markdown("✅ Blog generation completed!")
190
- if st.button("New Blog Generation"):
191
- self.session_history.clear()
192
- st.session_state.graph_state = None
193
- st.session_state.waiting_for_feedback = False
194
- st.rerun()
195
  except Exception as e:
196
- logger.error(f"Error in graph streaming: {e}")
197
- st.error(f"Error processing workflow: {e}")
198
 
199
  def _display_result(self, response):
200
- logger.info(f"Display result response: {response}")
 
201
  if self.usecase == "Blog Generation":
202
  messages = response.get("messages", [])
203
- if messages:
 
 
 
204
  content = messages[-1].content
205
- blog_content = response.get("blog_content", "")
206
 
207
- if blog_content and not st.session_state.content_displayed:
208
- st.markdown("### Generated Blog Content")
209
- st.markdown(self._format_blog_content(blog_content))
210
- st.session_state.content_displayed = True
211
- else:
212
- st.markdown(content)
213
 
214
  elif self.usecase == "Basic Chatbot":
215
  # Kept exactly as in original code
 
59
  if not st.session_state.waiting_for_feedback and st.session_state.graph_state:
60
  graph_state = st.session_state.graph_state
61
  if graph_state.next and graph_state.next[0] == "feedback_collector":
62
+ logger.info("\nSetting waiting_for_feedback based on graph state\n")
63
  st.session_state.waiting_for_feedback = True
64
 
65
  if st.session_state.waiting_for_feedback:
 
101
 
102
  def _process_feedback(self):
103
  latest_state = st.session_state.graph_state.values if st.session_state.graph_state else {}
104
+ logger.info(f"Latest state in _process_feedback: {latest_state}")
105
+
106
+ # Look for blog content in the right places
107
+ blog_content = latest_state.get("final_report", "")
108
 
109
  # Display the content if it hasn't been displayed yet
110
+ if blog_content and not st.session_state.content_displayed:
111
  with st.chat_message("assistant"):
112
  st.markdown("### Generated Blog Content")
113
+ st.markdown(self._format_blog_content(blog_content))
114
  st.session_state.content_displayed = True
115
+ logger.info("Blog content displayed from _process_feedback")
116
 
117
  # Check if we're at the feedback collection node
118
  current_node = st.session_state.graph_state.next[0] if st.session_state.graph_state.next else None
119
  logger.info(f"Current node in _process_feedback: {current_node}")
120
 
121
  if current_node == "feedback_collector":
122
+ # Only show feedback UI if we haven't already shown it
123
+ if not st.session_state.get("feedback_ui_shown", False):
124
+ st.write("### Review the generated content:")
125
+ col1, col2 = st.columns(2)
126
+
127
+ with col1:
128
+ if st.button("Approve", key="content_approve"):
129
+ feedback = {
130
+ "approved": True,
131
+ "comments": "Content approved."
132
+ }
133
+ self._submit_feedback(feedback)
134
+
135
+ with col2:
136
+ with st.expander("Request Revisions"):
137
+ comments = st.text_area("Provide revision comments:",
138
+ placeholder="Please explain what changes you would like to see.")
139
+ if st.button("Submit Revisions"):
140
+ if not comments:
141
+ st.error("Please provide revision comments.")
142
+ else:
143
+ feedback = {
144
+ "approved": False,
145
+ "comments": comments
146
+ }
147
+ self._submit_feedback(feedback)
148
+
149
+ st.session_state.feedback_ui_shown = True
150
 
151
  def _submit_feedback(self, feedback):
152
  """Submit feedback to the graph and continue processing."""
153
  try:
154
+ # Convert feedback to the expected format
155
  feedback_json = json.dumps(feedback)
156
  st.session_state.waiting_for_feedback = False
157
  st.session_state.content_displayed = False
158
+ st.session_state.feedback_ui_shown = False # Reset this flag
159
+
160
  # Continue processing with the feedback
161
  self._process_graph_stream(HumanMessage(content=feedback_json))
162
  logger.info(f"Feedback submitted: {feedback}")
 
177
  try:
178
  input_data = {"messages": [input_message]} if input_message else None
179
  for event in (self.graph.stream(input_data, self.config) if input_data else self.graph.stream(None, self.config)):
180
+ logger.info(f"\nGraph event: {event}\n")
181
+
182
+ # Check for blog content in each event
183
  for node, state in event.items():
184
+ # Print the full state for debugging
185
+ logger.info(f"\nState from {node}: {state}\n")
186
+
187
+ if "final_report" in state:
188
+ # Store the final report in session state for access in _process_feedback
189
+ if not st.session_state.get("blog_content"):
190
+ st.session_state.blog_content = state["final_report"]
191
+ logger.info(f"Stored blog content in session state: {state['final_report'][:30]}...")
192
+
193
  if "messages" in state and state["messages"]:
194
  with st.chat_message("assistant"):
195
+ content = state["messages"][-1].content
196
+ st.markdown(content)
197
+ self.session_history.add_ai_message(content)
198
 
199
  graph_state = self.graph.get_state(self.config)
200
+ logger.info(f"\nGraph state next: {graph_state.next}\n")
201
 
202
  if graph_state.next and graph_state.next[0] == "feedback_collector":
203
  st.session_state.waiting_for_feedback = True
204
  st.session_state.graph_state = graph_state
205
+ logger.info("\nPaused for feedback collection\n")
206
  st.rerun() # Force UI update to ensure feedback buttons appear
207
  break
 
 
 
 
 
 
 
 
 
 
208
  except Exception as e:
209
+ logger.error(f"\nError in graph streaming: {e}\n")
210
+ st.error(f"\nError processing workflow: {e}\n")
211
 
212
  def _display_result(self, response):
213
+ logger.info(f"\nDisplay result response: {response}\n")
214
+
215
  if self.usecase == "Blog Generation":
216
  messages = response.get("messages", [])
217
+ blog_content = response.get("final_report", "")
218
+
219
+ # If we have messages but no blog content, just show the message
220
+ if messages and not blog_content:
221
  content = messages[-1].content
222
+ st.markdown(content)
223
 
224
+ # No need to display blog content here - it will be handled by _process_feedback
225
+ # This prevents duplicate displays
226
+
227
+
 
 
228
 
229
  elif self.usecase == "Basic Chatbot":
230
  # Kept exactly as in original code