kamaleswar Mohanta commited on
Commit
39fa004
·
1 Parent(s): 13f57ba

check streamlit app

Browse files
Files changed (33) hide show
  1. src/__pycache__/__init__.cpython-312.pyc +0 -0
  2. src/langgraphagenticai/LLMS/__pycache__/__init__.cpython-312.pyc +0 -0
  3. src/langgraphagenticai/LLMS/__pycache__/chatgptllm.cpython-312.pyc +0 -0
  4. src/langgraphagenticai/LLMS/__pycache__/geminillm.cpython-312.pyc +0 -0
  5. src/langgraphagenticai/LLMS/__pycache__/groqllm.cpython-312.pyc +0 -0
  6. src/langgraphagenticai/__pycache__/__init__.cpython-312.pyc +0 -0
  7. src/langgraphagenticai/__pycache__/main.cpython-312.pyc +0 -0
  8. src/langgraphagenticai/graph/__pycache__/__init__.cpython-312.pyc +0 -0
  9. src/langgraphagenticai/graph/__pycache__/graph_builder.cpython-312.pyc +0 -0
  10. src/langgraphagenticai/graph/__pycache__/graph_builder_basic.cpython-312.pyc +0 -0
  11. src/langgraphagenticai/graph/__pycache__/graph_builder_blog.cpython-312.pyc +0 -0
  12. src/langgraphagenticai/graph/__pycache__/graph_builder_sdlc.cpython-312.pyc +0 -0
  13. src/langgraphagenticai/graph/__pycache__/graph_bulider_tool.cpython-312.pyc +0 -0
  14. src/langgraphagenticai/graph/graph_builder_sdlc.py +1 -1
  15. src/langgraphagenticai/logging/__pycache__/logging_utils.cpython-312.pyc +0 -0
  16. src/langgraphagenticai/nodes/__pycache__/__init__.cpython-312.pyc +0 -0
  17. src/langgraphagenticai/nodes/__pycache__/basic_chatbot_node.cpython-312.pyc +0 -0
  18. src/langgraphagenticai/nodes/__pycache__/blog_generation_node.cpython-312.pyc +0 -0
  19. src/langgraphagenticai/nodes/__pycache__/chatbot_with_Tool_node.cpython-312.pyc +0 -0
  20. src/langgraphagenticai/nodes/__pycache__/sdlc_node.cpython-312.pyc +0 -0
  21. src/langgraphagenticai/nodes/sdlc_node.py +69 -58
  22. src/langgraphagenticai/state/__pycache__/__init__.cpython-312.pyc +0 -0
  23. src/langgraphagenticai/state/__pycache__/state.cpython-312.pyc +0 -0
  24. src/langgraphagenticai/state/state.py +3 -32
  25. src/langgraphagenticai/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  26. src/langgraphagenticai/tools/__pycache__/search_tool.cpython-312.pyc +0 -0
  27. src/langgraphagenticai/ui/__pycache__/__init__.cpython-312.pyc +0 -0
  28. src/langgraphagenticai/ui/__pycache__/uiconfigfile.cpython-312.pyc +0 -0
  29. src/langgraphagenticai/ui/streamlitui/__pycache__/display_result.cpython-312.pyc +0 -0
  30. src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_blog.cpython-312.pyc +0 -0
  31. src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_sdlc.cpython-312.pyc +0 -0
  32. src/langgraphagenticai/ui/streamlitui/__pycache__/loadui.cpython-312.pyc +0 -0
  33. src/langgraphagenticai/ui/streamlitui/display_result_sdlc.py +151 -196
src/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/__pycache__/__init__.cpython-312.pyc and b/src/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/LLMS/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/LLMS/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/LLMS/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/LLMS/__pycache__/chatgptllm.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/LLMS/__pycache__/chatgptllm.cpython-312.pyc and b/src/langgraphagenticai/LLMS/__pycache__/chatgptllm.cpython-312.pyc differ
 
src/langgraphagenticai/LLMS/__pycache__/geminillm.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/LLMS/__pycache__/geminillm.cpython-312.pyc and b/src/langgraphagenticai/LLMS/__pycache__/geminillm.cpython-312.pyc differ
 
src/langgraphagenticai/LLMS/__pycache__/groqllm.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/LLMS/__pycache__/groqllm.cpython-312.pyc and b/src/langgraphagenticai/LLMS/__pycache__/groqllm.cpython-312.pyc differ
 
src/langgraphagenticai/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/__pycache__/main.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/__pycache__/main.cpython-312.pyc and b/src/langgraphagenticai/__pycache__/main.cpython-312.pyc differ
 
src/langgraphagenticai/graph/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/graph/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/graph/__pycache__/__init__.cpython-312.pyc differ
 
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/__pycache__/graph_builder_basic.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/graph/__pycache__/graph_builder_basic.cpython-312.pyc and b/src/langgraphagenticai/graph/__pycache__/graph_builder_basic.cpython-312.pyc differ
 
src/langgraphagenticai/graph/__pycache__/graph_builder_blog.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/graph/__pycache__/graph_builder_blog.cpython-312.pyc and b/src/langgraphagenticai/graph/__pycache__/graph_builder_blog.cpython-312.pyc differ
 
src/langgraphagenticai/graph/__pycache__/graph_builder_sdlc.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/graph/__pycache__/graph_builder_sdlc.cpython-312.pyc and b/src/langgraphagenticai/graph/__pycache__/graph_builder_sdlc.cpython-312.pyc differ
 
src/langgraphagenticai/graph/__pycache__/graph_bulider_tool.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/graph/__pycache__/graph_bulider_tool.cpython-312.pyc and b/src/langgraphagenticai/graph/__pycache__/graph_bulider_tool.cpython-312.pyc differ
 
src/langgraphagenticai/graph/graph_builder_sdlc.py CHANGED
@@ -41,7 +41,7 @@ class SdlcGraphBuilder:
41
  # Conditional edge after feedback processing
42
  graph_builder.add_conditional_edges(
43
  "ProcessFeedback",
44
- sldc_node.feedbackRoute,
45
  {
46
  "accept": END,
47
  "reject": "GenerateUserStories"
 
41
  # Conditional edge after feedback processing
42
  graph_builder.add_conditional_edges(
43
  "ProcessFeedback",
44
+ sldc_node.feedback_route,
45
  {
46
  "accept": END,
47
  "reject": "GenerateUserStories"
src/langgraphagenticai/logging/__pycache__/logging_utils.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/logging/__pycache__/logging_utils.cpython-312.pyc and b/src/langgraphagenticai/logging/__pycache__/logging_utils.cpython-312.pyc differ
 
src/langgraphagenticai/nodes/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/nodes/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/nodes/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/nodes/__pycache__/basic_chatbot_node.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/nodes/__pycache__/basic_chatbot_node.cpython-312.pyc and b/src/langgraphagenticai/nodes/__pycache__/basic_chatbot_node.cpython-312.pyc differ
 
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/__pycache__/chatbot_with_Tool_node.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/nodes/__pycache__/chatbot_with_Tool_node.cpython-312.pyc and b/src/langgraphagenticai/nodes/__pycache__/chatbot_with_Tool_node.cpython-312.pyc differ
 
src/langgraphagenticai/nodes/__pycache__/sdlc_node.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/nodes/__pycache__/sdlc_node.cpython-312.pyc and b/src/langgraphagenticai/nodes/__pycache__/sdlc_node.cpython-312.pyc differ
 
src/langgraphagenticai/nodes/sdlc_node.py CHANGED
@@ -137,65 +137,76 @@ class SdlcNode:
137
 
138
  @log_entry_exit
139
  def process_feedback(self, state: State) -> dict:
140
- """Process user feedback by adding it to the state."""
141
- current_stage_value = state.current_stage.value if isinstance(state.current_stage, SDLCStages) else state.current_stage
142
- logger.debug(f"Processing feedback for stage: {current_stage_value}")
143
-
144
- raw_feedback = state.feedback
145
- logger.debug(f"Raw feedback data: {raw_feedback}")
146
-
147
- # Only accept if feedback is {current_stage: ["accept"]}
148
- if (
149
- isinstance(raw_feedback, dict)
150
- and current_stage_value in raw_feedback
151
- and isinstance(raw_feedback[current_stage_value], list)
152
- and raw_feedback[current_stage_value]
153
- and raw_feedback[current_stage_value][-1].strip().lower() == "accept"
154
- ):
155
- state.feedback_decision = "accept"
156
- return {
157
- "feedback_decision": "accept",
158
- "feedback": state.feedback
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  }
160
- else:
161
- state.feedback_decision = "reject"
162
- return {
163
- "feedback_decision": "reject",
164
- "feedback": state.feedback
165
- }
166
-
167
  @log_entry_exit
168
- def feedbackRoute(self, state: State) -> str:
169
- """Route feedback based on SDLCState feedback format (Dict[str, List[str]]).
170
- Accept if feedback for current stage is empty or not present, reject if there are comments.
171
- """
172
- logger.debug(f"Routing feedback with state type: {type(state)}")
173
- logger.debug(f"State content: {state}")
174
 
175
- feedback_decision = None
176
-
177
- # If state is a dict with feedback_decision
178
- if isinstance(state, dict) and "feedback_decision" in state:
179
- feedback_decision = state["feedback_decision"]
180
- logger.debug(f"Found feedback_decision in state dict: {feedback_decision}")
181
- # If state is a SDLCState object
182
- elif isinstance(state, state.SDLCState):
183
- logger.debug(f"State is SDLCState object")
184
- if hasattr(state, "feedback_decision"):
185
- feedback_decision = state.feedback_decision
186
- logger.debug(f"Found feedback_decision as attribute: {feedback_decision}")
187
- # Try to get from node output
 
 
 
188
  else:
189
- logger.debug(f"State is neither dict nor SDLCState, trying alternative methods")
190
- try:
191
- if hasattr(state, "get"):
192
- node_output = state.get("ProcessFeedback", {})
193
- logger.debug(f"ProcessFeedback node output: {node_output}")
194
- feedback_decision = node_output.get("feedback_decision")
195
- logger.debug(f"Found feedback_decision in node output: {feedback_decision}")
196
- except Exception as e:
197
- logger.error(f"Error extracting feedback_decision: {e}")
198
-
199
- route = "accept" if feedback_decision == "accept" else "reject"
200
- logger.debug(f"Final routing decision: {route}")
201
- return route
 
137
 
138
  @log_entry_exit
139
  def process_feedback(self, state: State) -> dict:
140
+ """
141
+ Process user feedback passed via state and update state with decision.
142
+ Only { "current_stage": ["accept"] } ends the process.
143
+ """
144
+ logger.debug(f"--- Entering process_feedback ---")
145
+ logger.debug(f"Input state: {state.to_dict() if isinstance(state, State) else state}")
146
+
147
+ # Normalize current_stage to enum
148
+ if isinstance(state.current_stage, str):
149
+ try:
150
+ current_stage_enum = SDLCStages(state.current_stage)
151
+ except ValueError:
152
+ logger.warning(f"Unknown current_stage string: {state.current_stage}")
153
+ current_stage_enum = None
154
+ else:
155
+ current_stage_enum = state.current_stage
156
+
157
+ current_stage_value = current_stage_enum.value if current_stage_enum else state.current_stage
158
+
159
+ logger.debug(f"Processing feedback for stage: {current_stage_value}")
160
+
161
+ raw_feedback = state.feedback
162
+ logger.debug(f"Raw feedback data received in state: {raw_feedback}")
163
+
164
+ # Only accept if feedback is {current_stage: ["accept"]}
165
+ logger.debug(f"Checking acceptance: current_stage_value={current_stage_value}, raw_feedback={raw_feedback}")
166
+ if (
167
+ isinstance(raw_feedback, dict)
168
+ and current_stage_value in raw_feedback
169
+ and isinstance(raw_feedback[current_stage_value], list)
170
+ and raw_feedback[current_stage_value] # Check if list is not empty
171
+ and raw_feedback[current_stage_value][-1].strip().lower() == "accept"
172
+ ):
173
+ logger.info(f"Feedback for stage '{current_stage_value}' is ACCEPT. Ending flow.")
174
+ state.feedback_decision = "accept"
175
+ else:
176
+ logger.info(f"Feedback for stage '{current_stage_value}' is not accept. Looping back.")
177
+ state.feedback_decision = "reject"
178
+
179
+ return_value = {
180
+ "feedback_decision": state.feedback_decision,
181
+ "feedback": state.feedback # Pass the original feedback dict back
182
  }
183
+ logger.debug(f"Returning from process_feedback: {return_value}")
184
+ logger.debug(f"--- Exiting process_feedback ---")
185
+ return return_value
 
 
 
 
186
  @log_entry_exit
187
+ def feedback_route(self, state: State) -> str:
188
+ """Routes based on the feedback decision stored in the state."""
189
+ logger.debug(f"--- Entering feedback_route ---")
190
+ logger.debug(f"Routing feedback. Current state includes feedback_decision: {hasattr(state, 'feedback_decision')}")
 
 
191
 
192
+ logger.debug(f"Full state content received by feedback_route: {state.to_dict() if isinstance(state, SDLCState) else state}")
193
+
194
+ if not isinstance(state, State):
195
+ logger.error(f"Incorrect state type passed to feedback_route: {type(state)}. Defaulting to reject.")
196
+ logger.debug(f"--- Exiting feedback_route (routing: reject due to type error) ---")
197
+
198
+ return "reject"
199
+
200
+
201
+ feedback_decision = state.feedback_decision
202
+ logger.debug(f"Feedback decision read from state object: {feedback_decision}")
203
+
204
+ if feedback_decision == "accept":
205
+ logger.info("Feedback accepted. Routing to END.")
206
+ logger.debug(f"--- Exiting feedback_route (routing: accept) ---")
207
+ return "accept"
208
  else:
209
+
210
+ logger.info(f"Feedback decision is '{feedback_decision}'. Routing back for revision.")
211
+ logger.debug(f"--- Exiting feedback_route (routing: reject) ---")
212
+ return "reject"
 
 
 
 
 
 
 
 
 
src/langgraphagenticai/state/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/state/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/state/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/state/__pycache__/state.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/state/__pycache__/state.cpython-312.pyc and b/src/langgraphagenticai/state/__pycache__/state.cpython-312.pyc differ
 
src/langgraphagenticai/state/state.py CHANGED
@@ -82,38 +82,7 @@ class SDLCState(BaseModel):
82
  Level of Detail: Specific, quantifiable, and time-bound.
83
  Example Project: Developing a Mobile Application for a Local Bookstore
84
 
85
- Project Description: Develop a user-friendly mobile application for "The Book Nook," a local bookstore in Bangalore, to allow customers to browse their inventory, place orders online, and learn about upcoming events.
86
-
87
- Project Goals:
88
-
89
- Increase sales and revenue for The Book Nook.
90
- Enhance customer engagement and loyalty.
91
- Modernize The Book Nook's presence and reach a wider audience in Bangalore.
92
- Project Scope:
93
-
94
- Inclusions:
95
- Developing a mobile application compatible with Android and iOS.
96
- Features: Browsing book catalog with search and filtering, viewing book details (description, author, price, availability), creating user accounts, adding books to a shopping cart, secure online payment integration, order history, push notifications for new arrivals and events, information about store hours and location.
97
- Integration with the bookstore's existing inventory management system.
98
- Basic user support documentation.
99
- Exclusions:
100
- Developing a separate tablet application.
101
- Implementing a loyalty points program (will be considered in a future phase).
102
- Integrating with social media platforms for direct purchasing.
103
- Providing real-time inventory updates beyond a daily sync.
104
- Developing advanced analytics dashboards for the bookstore owner in this phase.
105
- Project Objectives (SMART):
106
-
107
- Increase online sales by 15% within the first six months of the app launch (Measurable, Achievable, Relevant, Time-bound).
108
- Achieve an average user rating of 4.5 stars or higher on both app stores within three months of launch (Measurable, Achievable, Relevant, Time-bound).
109
- Acquire 500 new registered app users within the first month of launch (Measurable, Achievable, Relevant, Time-bound).
110
- Successfully integrate the app with the existing inventory system with no data loss by the end of the development phase (Measurable, Achievable, Relevant, Time-bound).
111
- In Summary:
112
-
113
- The description gives a general idea of the project.
114
- The goals state the desired high-level outcomes.
115
- The scope defines the boundaries of the work to be done.
116
- The objectives provide specific, measurable steps to achieve the goals within the defined scope.  
117
  """
118
 
119
  # core state attributes
@@ -140,6 +109,7 @@ The objectives provide specific, measurable steps to achieve the goals within th
140
 
141
  # Feedback
142
  feedback: Dict[str, List[str]] = Field(default_factory=dict, description="User feedback by stage")
 
143
 
144
  # Metadata
145
  created_at: str = Field(default_factory=lambda: datetime.now().isoformat(), description="Creation timestamp")
@@ -167,6 +137,7 @@ The objectives provide specific, measurable steps to achieve the goals within th
167
  # Update stage
168
  self.current_stage = new_stage
169
  self.last_updated = datetime.now().isoformat()
 
170
  def add_feedback(self, stage: SDLCStages, feedback_text: str):
171
  """
172
  Add feedback for a specific stage.
 
82
  Level of Detail: Specific, quantifiable, and time-bound.
83
  Example Project: Developing a Mobile Application for a Local Bookstore
84
 
85
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  """
87
 
88
  # core state attributes
 
109
 
110
  # Feedback
111
  feedback: Dict[str, List[str]] = Field(default_factory=dict, description="User feedback by stage")
112
+ feedback_decision: Optional[str] = Field(None, description="Decision after processing feedback (accept/reject)")
113
 
114
  # Metadata
115
  created_at: str = Field(default_factory=lambda: datetime.now().isoformat(), description="Creation timestamp")
 
137
  # Update stage
138
  self.current_stage = new_stage
139
  self.last_updated = datetime.now().isoformat()
140
+
141
  def add_feedback(self, stage: SDLCStages, feedback_text: str):
142
  """
143
  Add feedback for a specific stage.
src/langgraphagenticai/tools/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/tools/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/tools/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/tools/__pycache__/search_tool.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/tools/__pycache__/search_tool.cpython-312.pyc and b/src/langgraphagenticai/tools/__pycache__/search_tool.cpython-312.pyc differ
 
src/langgraphagenticai/ui/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/ui/__pycache__/__init__.cpython-312.pyc and b/src/langgraphagenticai/ui/__pycache__/__init__.cpython-312.pyc differ
 
src/langgraphagenticai/ui/__pycache__/uiconfigfile.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/ui/__pycache__/uiconfigfile.cpython-312.pyc and b/src/langgraphagenticai/ui/__pycache__/uiconfigfile.cpython-312.pyc differ
 
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/__pycache__/display_result_blog.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_blog.cpython-312.pyc and b/src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_blog.cpython-312.pyc differ
 
src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_sdlc.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_sdlc.cpython-312.pyc and b/src/langgraphagenticai/ui/streamlitui/__pycache__/display_result_sdlc.cpython-312.pyc differ
 
src/langgraphagenticai/ui/streamlitui/__pycache__/loadui.cpython-312.pyc CHANGED
Binary files a/src/langgraphagenticai/ui/streamlitui/__pycache__/loadui.cpython-312.pyc and b/src/langgraphagenticai/ui/streamlitui/__pycache__/loadui.cpython-312.pyc differ
 
src/langgraphagenticai/ui/streamlitui/display_result_sdlc.py CHANGED
@@ -9,13 +9,13 @@ import functools
9
  import time
10
  import base64
11
  from src.langgraphagenticai.logging.logging_utils import logger, log_entry_exit
12
- import os # Import the os module
13
- from langgraph.graph import END # Import END for checking graph completion
14
 
15
  # --- Pydantic Model for Feedback ---
16
  class ReviewFeedback(BaseModel):
17
  approved: bool = Field(description="Approval status: True for approved, False for rejected")
18
- comments: str = Field(description="Reviewer comments", default="") # Default to empty string
19
 
20
  # --- Main Display Class ---
21
  class DisplaySdlcResult:
@@ -27,20 +27,21 @@ class DisplaySdlcResult:
27
  def _initialize_session_state(self):
28
  """Initialize session state variables if they don't exist."""
29
  defaults = {
30
- "sdlc_stage": "planning", # Tracks the conceptual stage
31
  "project_name": "",
32
  "project_description": "",
33
  "project_goals": "",
34
  "project_scope": "",
35
  "project_objectives": "",
36
- "requirements_generated": False, # UI flag: Have requirements been generated at least once?
37
- "user_stories_generated": False, # UI flag: Have user stories been generated and are ready for review/approved?
38
- "generated_requirements": None, # Stores the actual artifact text
39
- "generated_user_stories": None, # Stores the actual artifact text
40
- "feedback": None, # Stores feedback dict for process_feedback node (transient)
41
- "needs_resume_after_feedback": False, # Flag to trigger graph resume
42
- "graph_completed": False, # Flag to indicate workflow completion (reached END)
43
- "graph_running": False, # Flag to prevent concurrent runs/UI updates during stream
 
44
  }
45
  for key, value in defaults.items():
46
  if key not in st.session_state:
@@ -51,60 +52,55 @@ class DisplaySdlcResult:
51
  """Manages the overall SDLC workflow display and interaction in Streamlit."""
52
  st.title("Software Development Life Cycle (SDLC) Workflow")
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  phases = ["Planning", "Design", "Development", "Testing", "Deployment"]
55
  icons = ["🗓️", "📐", "💻", "🧪", "🚀"]
56
  tabs = st.tabs([f"{icon} {phase}" for icon, phase in zip(icons, phases)])
57
 
58
- # --- Check if we need to resume the graph ---
59
- needs_resume = st.session_state.get("needs_resume_after_feedback", False)
60
- if needs_resume and not st.session_state.get("graph_running", False):
61
- logger.info("Detected need to resume graph after feedback.")
62
- st.session_state["needs_resume_after_feedback"] = False # Reset the flag immediately
63
- st.session_state["graph_running"] = True # Set lock
64
- self._resume_sdlc_graph() # Call the function to handle resumption
65
- st.session_state["graph_running"] = False # Release lock
66
- # Rerun to update the UI based on the resumed graph's state changes
67
- st.rerun()
68
- return # Stop further processing in this run
69
-
70
- # --- Display content based on the current state ---
71
- with tabs[0]: # Planning Tab
72
- # Only show collection form if requirements haven't been generated yet
73
  if not st.session_state.get("requirements_generated"):
74
- self._display_planning_phase() # Contains _collect_project_requirements
75
- # Always display artifacts and feedback form if artifacts exist
76
- # The feedback form is only active if stories are generated
77
  self._display_planning_artifacts()
78
 
79
- # --- Placeholder Tabs ---
80
  with tabs[1]: self._display_design_phase(); self._display_design_artifacts()
81
  with tabs[2]: self._display_development_phase(); self._display_development_artifacts()
82
  with tabs[3]: self._display_testing_phase(); self._display_testing_artifacts()
83
  with tabs[4]: self._display_deployment_phase(); self._display_deployment_artifacts()
84
 
85
- # --- Display Completion Status ---
86
  if st.session_state.get("graph_completed"):
87
  st.success("✅ SDLC Planning Phase Completed Successfully!")
 
88
 
89
- # --- Restart Button ---
90
  st.markdown("---")
91
  if st.button("Restart SDLC Workflow", key="restart_button"):
 
92
  self._reset_session_state()
93
  st.rerun()
94
 
95
  @log_entry_exit
96
  def _reset_session_state(self):
97
  """Resets relevant session state keys to start the workflow over."""
98
- keys_to_reset = list(st.session_state.keys()) # Get all keys
99
  logger.info("Resetting session state.")
100
  for key in keys_to_reset:
101
- # Avoid deleting internal Streamlit keys or keys we might want to persist
102
- if not key.startswith("_"): # Basic check, adjust if needed
103
- del st.session_state[key]
104
- # Re-initialize after clearing
105
  self._initialize_session_state()
106
 
107
-
108
  @log_entry_exit
109
  def _display_planning_phase(self):
110
  """Displays the requirement collection section."""
@@ -116,13 +112,12 @@ class DisplaySdlcResult:
116
  """Displays generated planning artifacts and the feedback form."""
117
  st.subheader("Planning Artifacts")
118
  requirements_exist = st.session_state.get("requirements_generated")
119
- stories_exist = st.session_state.get("user_stories_generated") # This flag indicates readiness for review/approval
120
 
121
- # Ensure feedback is initialized as a dictionary
122
  if "feedback" not in st.session_state or st.session_state["feedback"] is None:
123
  st.session_state["feedback"] = {}
 
124
 
125
- # Display Requirements if they exist
126
  if requirements_exist:
127
  with st.expander("Generated Requirements", expanded=True):
128
  st.markdown(st.session_state["generated_requirements"])
@@ -131,14 +126,12 @@ class DisplaySdlcResult:
131
  else:
132
  st.info("Requirements will be displayed here after generation.")
133
 
134
- # Display User Stories and Feedback Form ONLY if stories are generated and ready for review
135
  if stories_exist:
136
  with st.expander("Generated User Stories", expanded=True):
137
  st.markdown(st.session_state["generated_user_stories"])
138
  if st.button("Save User Stories", key="save_user_stories_planning"):
139
  self._save_artifact(st.session_state["generated_user_stories"], "user_stories.txt")
140
 
141
- # --- Feedback Section ---
142
  if not st.session_state.get("graph_completed"):
143
  st.markdown("### Feedback on User Stories")
144
  decision_options = ("Approve", "Reject")
@@ -147,56 +140,126 @@ class DisplaySdlcResult:
147
  decision_options,
148
  key="user_story_feedback_decision",
149
  horizontal=True,
150
- index=0 # Default to Approve
151
  )
152
-
153
  comments = st.text_area(
154
  "Comments (Required for Rejection):",
155
- # value="Include Subscriptions model to retain customer", # Example default
156
  placeholder="Include Subscriptions model to retain customer",
157
  key="user_story_feedback_comments"
158
  )
159
-
160
- if st.button("Submit Review", key="submit_review_button"):
 
 
 
 
161
  if selected_decision == "Reject" and not comments.strip():
162
  st.error("Comments are required when rejecting.")
 
163
  else:
164
  is_approved = (selected_decision == "Approve")
165
  logger.info(f"Feedback submitted: {'Approved' if is_approved else 'Rejected with comments.'}")
166
-
167
- # Structure feedback to match SDLCState format
168
- current_stage = "planning" # Assuming feedback is always on Planning in this example
169
- new_feedback = {
170
- current_stage: [comments] if not is_approved else [] # Only store comments if rejected
171
  }
172
-
173
- # Update session state with the new feedback structure
174
- if current_stage not in st.session_state["feedback"]:
175
- st.session_state["feedback"][current_stage] = ["accept"]
176
-
177
- if not is_approved:
178
- st.session_state["feedback"][current_stage].append(comments)
179
-
180
- logger.info("Session state updated with feedback: %s", st.session_state["feedback"])
181
-
182
- # --- Signal that the graph needs to resume ---
183
  st.session_state["needs_resume_after_feedback"] = True
184
-
185
- # If rejected, immediately hide the stories from the UI until regenerated
186
  if not is_approved:
187
  st.session_state["user_stories_generated"] = False
188
-
189
- logger.info("Feedback stored. Triggering rerun for graph resumption.")
190
  st.rerun()
191
  else:
192
- st.success("User stories approved.") # Show if graph is completed
 
193
  elif requirements_exist and not stories_exist:
194
  st.info("User stories will be displayed here after generation or if awaiting regeneration after feedback.")
195
  elif not requirements_exist and not stories_exist:
196
  st.info("No artifacts generated yet in the Planning phase.")
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
- # --- Placeholder Display Functions for Other Phases ---
200
  @log_entry_exit
201
  def _display_design_phase(self):
202
  st.header("Design Phase")
@@ -237,11 +300,9 @@ class DisplaySdlcResult:
237
  st.subheader("Deployment Artifacts")
238
  st.info("Deployment artifacts (e.g., deployment plans, monitoring dashboards) will be displayed here.")
239
 
240
- # --- Requirement Collection Form ---
241
  @log_entry_exit
242
  def _collect_project_requirements(self):
243
  """Displays the form to collect initial project details."""
244
-
245
  DefaultProjectName = "The Book Nook"
246
  DefaultDescription = """Develop a user-friendly mobile application for "The Book Nook," a local bookstore in Bangalore,
247
  to allow customers to browse their inventory, place orders online, and learn about upcoming events."""
@@ -273,20 +334,18 @@ class DisplaySdlcResult:
273
  project_objectives = st.text_area("Project Objectives", value=st.session_state.get("project_objectives", DefaultObjectives), placeholder=DefaultObjectives, height=150)
274
 
275
  submit = st.form_submit_button("Generate Requirements & User Stories")
276
- if submit and not st.session_state.get("graph_running", False):
277
- # Update session state with form values
278
  st.session_state["project_name"] = project_name.strip() or DefaultProjectName
279
  st.session_state["project_description"] = project_description.strip() or DefaultDescription
280
  st.session_state["project_goals"] = project_goals.strip() or DefaultGoals
281
  st.session_state["project_scope"] = project_scope.strip() or DefaultScope
282
  st.session_state["project_objectives"] = project_objectives.strip() or DefaultObjectives
283
- st.session_state["sdlc_stage"] = "planning" # Ensure stage is set
284
-
285
  logger.info("Initial project details submitted. Running graph for the first time.")
286
- st.session_state["graph_running"] = True # Set lock
287
- self._run_sdlc_graph_initial() # Call the initial run function
288
- st.session_state["graph_running"] = False # Release lock
289
- st.rerun() # Rerun to display the generated artifacts
290
 
291
  @log_entry_exit
292
  def _run_sdlc_graph_initial(self):
@@ -306,134 +365,31 @@ class DisplaySdlcResult:
306
  try:
307
  for event in self.graph.stream(input_data, self.config):
308
  logger.info(f"Initial Run Event: {event}")
309
- event_key = list(event.keys())[0]
310
-
311
-
312
  if event_key == "__interrupt__":
313
- logger.info("Graph interrupted as expected after generating artifacts.")
314
- last_node_state = event.get("__interrupt__")
315
- if last_node_state:
316
- requirements = last_node_state.get("generated_requirements", requirements)
317
- user_stories = last_node_state.get("generated_user_stories", user_stories)
318
- break # Exit loop after interrupt is handled
319
-
320
- # Process node updates
321
  for node, state in event.items():
322
  if state is None: continue
323
  logger.info(f"Node '{node}' updated state.")
324
- # Capture the latest generated artifacts from the state
325
  if "generated_requirements" in state:
326
  requirements = state["generated_requirements"]
327
  if "user_stories" in state:
328
  user_stories = state["user_stories"]
329
-
330
  except Exception as e:
331
- logger.error(f"Error during initial graph stream: {e}", exc_info=True)
332
- st.error(f"An error occurred during graph execution: {e}")
333
- return # Stop processing if stream fails
334
-
335
-
336
- st.session_state["generated_requirements"] = requirements
337
- st.session_state["generated_user_stories"] = user_stories
338
- st.session_state["requirements_generated"] = requirements is not None
339
- st.session_state["user_stories_generated"] = user_stories is not None # Mark stories as generated and ready for review
340
- logger.info("Initial graph run finished (interrupted). Artifacts stored in session state.")
341
-
342
-
343
-
344
- @log_entry_exit
345
- def _resume_sdlc_graph(self):
346
- """Resumes the graph execution from the checkpoint after feedback."""
347
- logger.info("Resuming SDLC graph execution...")
348
-
349
- # Read feedback from session state
350
- feedback_data = st.session_state.get("feedback")
351
- logger.info(f"Feedback data read from session state: {feedback_data}")
352
-
353
- # # Clear feedback from session state BEFORE calling stream
354
- # if "feedback" in st.session_state:
355
- # try:
356
- # del st.session_state["feedback"]
357
- # logger.info("Feedback cleared from session state before resuming graph.")
358
- # except KeyError:
359
- # logger.warning("Attempted to delete 'feedback' from session state, but it was already gone.")
360
-
361
- # Prepare input data for the resume call
362
- resume_input_data = {}
363
- if feedback_data:
364
- resume_input_data["feedback"] = feedback_data
365
- else:
366
- logger.warning("Resume triggered, but no feedback data found. Using empty input.")
367
-
368
- # Log checkpoint state for debugging
369
- thread_id = self.config.get("configurable", {}).get("thread_id", "N/A")
370
- checkpoint = None
371
- if hasattr(self.graph, 'checkpointer'):
372
- try:
373
- checkpoint_tuple = self.graph.checkpointer.get_tuple({"configurable": {"thread_id": thread_id}})
374
- if checkpoint_tuple:
375
- checkpoint = checkpoint_tuple.checkpoint
376
- logger.info(f"Checkpoint retrieved: {checkpoint}")
377
- next_node = checkpoint.get("next_node", "Unknown")
378
- logger.info(f"Next node from checkpoint: {next_node}")
379
- else:
380
- logger.warning("No checkpoint found for thread_id: %s", thread_id)
381
- except Exception as e:
382
- logger.error(f"Failed to retrieve checkpoint: {e}", exc_info=True)
383
-
384
- requirements = st.session_state.get("generated_requirements")
385
- user_stories = st.session_state.get("generated_user_stories")
386
- graph_completed_flag = False
387
-
388
- with st.spinner("Processing feedback and continuing workflow..."):
389
- try:
390
- # Prefer resume if available
391
- if hasattr(self.graph, "resume") and checkpoint:
392
- logger.info("Resuming graph from checkpoint using .resume()")
393
- for event in self.graph.resume(checkpoint, resume_input_data, self.config):
394
- logger.info(f"Resume Event: {event}")
395
- # Process node updates
396
- for node, state in event.items():
397
- if state is None: continue
398
- logger.info(f"Node '{node}' updated state during resume.")
399
- if "generated_requirements" in state:
400
- requirements = state["generated_requirements"]
401
- if "user_stories" in state:
402
- user_stories = state["user_stories"]
403
- if node == END or node == "END":
404
- graph_completed_flag = True
405
- else:
406
- # Fallback to stream
407
- for event in self.graph.stream(resume_input_data, self.config):
408
- logger.info(f"Resume Event: {event}")
409
- # Process node updates
410
- for node, state in event.items():
411
- if state is None: continue
412
- logger.info(f"Node '{node}' updated state during resume.")
413
- if "generated_requirements" in state:
414
- requirements = state["generated_requirements"]
415
- if "user_stories" in state:
416
- user_stories = state["user_stories"]
417
- if node == END or node == "END":
418
- graph_completed_flag = True
419
- except Exception as e:
420
- logger.error(f"Error during graph resume stream: {e}", exc_info=True)
421
- st.error(f"An error occurred during graph resumption: {e}")
422
  return
423
 
424
- # Update session state after resume processing
425
  st.session_state["generated_requirements"] = requirements
426
  st.session_state["generated_user_stories"] = user_stories
427
  st.session_state["requirements_generated"] = requirements is not None
428
  st.session_state["user_stories_generated"] = user_stories is not None
429
- st.session_state["graph_completed"] = graph_completed_flag
430
-
431
- logger.info("Graph resumption processing finished.")
432
-
433
-
434
-
435
-
436
-
437
 
438
  @log_entry_exit
439
  def _save_artifact(self, content: str, filename: str):
@@ -445,5 +401,4 @@ class DisplaySdlcResult:
445
  logger.info(f"Generated download link for {filename}")
446
  except Exception as e:
447
  logger.error(f"Error creating download link for {filename}: {e}")
448
- st.error(f"Could not generate download link for {filename}.")
449
-
 
9
  import time
10
  import base64
11
  from src.langgraphagenticai.logging.logging_utils import logger, log_entry_exit
12
+ import os
13
+ from langgraph.graph import END
14
 
15
  # --- Pydantic Model for Feedback ---
16
  class ReviewFeedback(BaseModel):
17
  approved: bool = Field(description="Approval status: True for approved, False for rejected")
18
+ comments: str = Field(description="Reviewer comments", default="")
19
 
20
  # --- Main Display Class ---
21
  class DisplaySdlcResult:
 
27
  def _initialize_session_state(self):
28
  """Initialize session state variables if they don't exist."""
29
  defaults = {
30
+ "sdlc_stage": "planning",
31
  "project_name": "",
32
  "project_description": "",
33
  "project_goals": "",
34
  "project_scope": "",
35
  "project_objectives": "",
36
+ "requirements_generated": False,
37
+ "user_stories_generated": False,
38
+ "generated_requirements": None,
39
+ "generated_user_stories": None,
40
+ "feedback": None,
41
+ "needs_resume_after_feedback": False,
42
+ "graph_completed": False,
43
+ "graph_running": False,
44
+ "feedback_pending": False,
45
  }
46
  for key, value in defaults.items():
47
  if key not in st.session_state:
 
52
  """Manages the overall SDLC workflow display and interaction in Streamlit."""
53
  st.title("Software Development Life Cycle (SDLC) Workflow")
54
 
55
+ # Prevent concurrent runs
56
+ if st.session_state.get("graph_running"):
57
+ st.warning("Workflow is already running. Please wait.")
58
+ logger.warning("Workflow blocked due to graph_running=True")
59
+ return
60
+
61
+ # Handle resumption
62
+ if st.session_state.get("needs_resume_after_feedback"):
63
+ logger.info("Detected need to resume graph after feedback.")
64
+ st.session_state["graph_running"] = True
65
+ self._resume_sdlc_graph()
66
+ st.session_state["graph_running"] = False
67
+ return # Avoid further processing to prevent UI conflicts
68
+
69
+ # Display UI
70
  phases = ["Planning", "Design", "Development", "Testing", "Deployment"]
71
  icons = ["🗓️", "📐", "💻", "🧪", "🚀"]
72
  tabs = st.tabs([f"{icon} {phase}" for icon, phase in zip(icons, phases)])
73
 
74
+ with tabs[0]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  if not st.session_state.get("requirements_generated"):
76
+ self._display_planning_phase()
 
 
77
  self._display_planning_artifacts()
78
 
 
79
  with tabs[1]: self._display_design_phase(); self._display_design_artifacts()
80
  with tabs[2]: self._display_development_phase(); self._display_development_artifacts()
81
  with tabs[3]: self._display_testing_phase(); self._display_testing_artifacts()
82
  with tabs[4]: self._display_deployment_phase(); self._display_deployment_artifacts()
83
 
 
84
  if st.session_state.get("graph_completed"):
85
  st.success("✅ SDLC Planning Phase Completed Successfully!")
86
+ st.markdown("You can restart the workflow or review the artifacts.")
87
 
 
88
  st.markdown("---")
89
  if st.button("Restart SDLC Workflow", key="restart_button"):
90
+ logger.info("Restart SDLC Workflow button clicked")
91
  self._reset_session_state()
92
  st.rerun()
93
 
94
  @log_entry_exit
95
  def _reset_session_state(self):
96
  """Resets relevant session state keys to start the workflow over."""
97
+ keys_to_reset = list(st.session_state.keys())
98
  logger.info("Resetting session state.")
99
  for key in keys_to_reset:
100
+ if not key.startswith("_"):
101
+ del st.session_state[key]
 
 
102
  self._initialize_session_state()
103
 
 
104
  @log_entry_exit
105
  def _display_planning_phase(self):
106
  """Displays the requirement collection section."""
 
112
  """Displays generated planning artifacts and the feedback form."""
113
  st.subheader("Planning Artifacts")
114
  requirements_exist = st.session_state.get("requirements_generated")
115
+ stories_exist = st.session_state.get("user_stories_generated")
116
 
 
117
  if "feedback" not in st.session_state or st.session_state["feedback"] is None:
118
  st.session_state["feedback"] = {}
119
+ logger.info("Initialized empty feedback dictionary in session state.")
120
 
 
121
  if requirements_exist:
122
  with st.expander("Generated Requirements", expanded=True):
123
  st.markdown(st.session_state["generated_requirements"])
 
126
  else:
127
  st.info("Requirements will be displayed here after generation.")
128
 
 
129
  if stories_exist:
130
  with st.expander("Generated User Stories", expanded=True):
131
  st.markdown(st.session_state["generated_user_stories"])
132
  if st.button("Save User Stories", key="save_user_stories_planning"):
133
  self._save_artifact(st.session_state["generated_user_stories"], "user_stories.txt")
134
 
 
135
  if not st.session_state.get("graph_completed"):
136
  st.markdown("### Feedback on User Stories")
137
  decision_options = ("Approve", "Reject")
 
140
  decision_options,
141
  key="user_story_feedback_decision",
142
  horizontal=True,
143
+ index=0
144
  )
 
145
  comments = st.text_area(
146
  "Comments (Required for Rejection):",
 
147
  placeholder="Include Subscriptions model to retain customer",
148
  key="user_story_feedback_comments"
149
  )
150
+ submit_disabled = st.session_state.get("feedback_pending", False) or st.session_state.get("graph_running", False)
151
+ if st.button("Submit Review", key="submit_review_button", disabled=submit_disabled):
152
+ if st.session_state.get("feedback_pending"):
153
+ st.warning("Feedback is already being processed. Please wait.")
154
+ return
155
+ st.session_state["feedback_pending"] = True
156
  if selected_decision == "Reject" and not comments.strip():
157
  st.error("Comments are required when rejecting.")
158
+ st.session_state["feedback_pending"] = False
159
  else:
160
  is_approved = (selected_decision == "Approve")
161
  logger.info(f"Feedback submitted: {'Approved' if is_approved else 'Rejected with comments.'}")
162
+ current_stage = "planning"
163
+ st.session_state["feedback"] = {
164
+ current_stage: ["accept"] if is_approved else [comments.strip()]
 
 
165
  }
166
+ logger.info("Feedback stored: %s", st.session_state["feedback"])
 
 
 
 
 
 
 
 
 
 
167
  st.session_state["needs_resume_after_feedback"] = True
 
 
168
  if not is_approved:
169
  st.session_state["user_stories_generated"] = False
170
+ st.session_state["feedback_pending"] = False
 
171
  st.rerun()
172
  else:
173
+ st.success("User stories approved. Planning phase completed.")
174
+ st.markdown("The feedback form is disabled as the workflow is complete.")
175
  elif requirements_exist and not stories_exist:
176
  st.info("User stories will be displayed here after generation or if awaiting regeneration after feedback.")
177
  elif not requirements_exist and not stories_exist:
178
  st.info("No artifacts generated yet in the Planning phase.")
179
 
180
+ @log_entry_exit
181
+ def _resume_sdlc_graph(self):
182
+ """Resumes the graph execution from the checkpoint after feedback."""
183
+ logger.info("Resuming SDLC graph execution...")
184
+ logger.info("Session state before resumption: %s", st.session_state)
185
+ feedback_data = st.session_state.get("feedback")
186
+ logger.info(f"Feedback data: %s", feedback_data)
187
+
188
+ # Prepare input data with project details and feedback
189
+ resume_input_data = {
190
+ "feedback": feedback_data,
191
+ "project_name": st.session_state.get("project_name"),
192
+ "project_description": st.session_state.get("project_description"),
193
+ "project_goals": st.session_state.get("project_goals"),
194
+ "project_scope": st.session_state.get("project_scope"),
195
+ "project_objectives": st.session_state.get("project_objectives"),
196
+ "generated_requirements": st.session_state.get("generated_requirements"),
197
+ "generated_user_stories": st.session_state.get("generated_user_stories"),
198
+ }
199
+
200
+ thread_id = self.config.get("configurable", {}).get("thread_id", "N/A")
201
+ checkpoint = None
202
+ if hasattr(self.graph, 'checkpointer'):
203
+ checkpoint_tuple = self.graph.checkpointer.get_tuple({"configurable": {"thread_id": thread_id}})
204
+ if checkpoint_tuple:
205
+ checkpoint = checkpoint_tuple.checkpoint
206
+ logger.info(f"Checkpoint retrieved: %s", checkpoint)
207
+ else:
208
+ logger.error("No checkpoint found for thread_id: %s", thread_id)
209
+ st.error("No valid checkpoint found. Please restart the workflow.")
210
+ return
211
+ else:
212
+ logger.error("Graph checkpointer not available.")
213
+ st.error("Graph configuration error. Please restart the workflow.")
214
+ return
215
+
216
+ requirements = st.session_state.get("generated_requirements")
217
+ user_stories = st.session_state.get("generated_user_stories")
218
+ graph_completed_flag = False
219
+
220
+ with st.spinner("Processing feedback and continuing workflow..."):
221
+ try:
222
+ # Stream from checkpoint with feedback and project details
223
+ for event in self.graph.stream(resume_input_data, config=self.config):
224
+ logger.info(f"Resume Event: %s", json.dumps(event, default=str))
225
+ for node, state in event.items():
226
+ if state is None:
227
+ logger.info(f"Node '{node}' has null state, skipping.")
228
+ continue
229
+ # Handle tuple or other non-dict states
230
+ if not isinstance(state, dict):
231
+ logger.warning(f"Node '{node}' state is not a dict: {type(state)}. Skipping state processing.")
232
+ continue
233
+ logger.info(f"Node '{node}' updated state: %s", state)
234
+ if "generated_requirements" in state:
235
+ requirements = state["generated_requirements"]
236
+ if "user_stories" in state:
237
+ user_stories = state["user_stories"]
238
+ if "feedback_decision" in state:
239
+ logger.info("Feedback decision: %s", state["feedback_decision"])
240
+ # Check for END condition
241
+ if node == END or node == "__end__" or state.get("next_node") == END:
242
+ logger.info("Graph reached END node")
243
+ graph_completed_flag = True
244
+ # Handle interrupt for feedback
245
+ if node == "__interrupt__":
246
+ logger.info("Graph interrupted for feedback processing")
247
+ except Exception as e:
248
+ logger.error(f"Error during graph stream: %s", e, exc_info=True)
249
+ st.error(f"An error occurred during graph resumption: {e}")
250
+ return
251
+
252
+ # Update session state
253
+ st.session_state["generated_requirements"] = requirements
254
+ st.session_state["generated_user_stories"] = user_stories
255
+ st.session_state["requirements_generated"] = requirements is not None
256
+ st.session_state["user_stories_generated"] = user_stories is not None and not graph_completed_flag
257
+ st.session_state["graph_completed"] = graph_completed_flag
258
+ st.session_state["needs_resume_after_feedback"] = False
259
+ st.session_state["feedback"] = {}
260
+ logger.info("Graph resumption completed. Graph completed: %s", graph_completed_flag)
261
+ logger.info("Session state after resumption: %s", st.session_state)
262
 
 
263
  @log_entry_exit
264
  def _display_design_phase(self):
265
  st.header("Design Phase")
 
300
  st.subheader("Deployment Artifacts")
301
  st.info("Deployment artifacts (e.g., deployment plans, monitoring dashboards) will be displayed here.")
302
 
 
303
  @log_entry_exit
304
  def _collect_project_requirements(self):
305
  """Displays the form to collect initial project details."""
 
306
  DefaultProjectName = "The Book Nook"
307
  DefaultDescription = """Develop a user-friendly mobile application for "The Book Nook," a local bookstore in Bangalore,
308
  to allow customers to browse their inventory, place orders online, and learn about upcoming events."""
 
334
  project_objectives = st.text_area("Project Objectives", value=st.session_state.get("project_objectives", DefaultObjectives), placeholder=DefaultObjectives, height=150)
335
 
336
  submit = st.form_submit_button("Generate Requirements & User Stories")
337
+ if submit and not st.session_state.get("graph_running"):
 
338
  st.session_state["project_name"] = project_name.strip() or DefaultProjectName
339
  st.session_state["project_description"] = project_description.strip() or DefaultDescription
340
  st.session_state["project_goals"] = project_goals.strip() or DefaultGoals
341
  st.session_state["project_scope"] = project_scope.strip() or DefaultScope
342
  st.session_state["project_objectives"] = project_objectives.strip() or DefaultObjectives
343
+ st.session_state["sdlc_stage"] = "planning"
 
344
  logger.info("Initial project details submitted. Running graph for the first time.")
345
+ st.session_state["graph_running"] = True
346
+ self._run_sdlc_graph_initial()
347
+ st.session_state["graph_running"] = False
348
+ st.rerun()
349
 
350
  @log_entry_exit
351
  def _run_sdlc_graph_initial(self):
 
365
  try:
366
  for event in self.graph.stream(input_data, self.config):
367
  logger.info(f"Initial Run Event: {event}")
368
+ event_key = list(event.keys())[0]
 
 
369
  if event_key == "__interrupt__":
370
+ logger.info("Graph interrupted as expected after generating artifacts.")
371
+ last_node_state = event.get("__interrupt__")
372
+ if last_node_state:
373
+ requirements = last_node_state.get("generated_requirements", requirements)
374
+ user_stories = last_node_state.get("generated_user_stories", user_stories)
375
+ break
 
 
376
  for node, state in event.items():
377
  if state is None: continue
378
  logger.info(f"Node '{node}' updated state.")
 
379
  if "generated_requirements" in state:
380
  requirements = state["generated_requirements"]
381
  if "user_stories" in state:
382
  user_stories = state["user_stories"]
 
383
  except Exception as e:
384
+ logger.error(f"Error during initial graph stream: {e}", exc_info=True)
385
+ st.error(f"An error occurred during graph execution: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  return
387
 
 
388
  st.session_state["generated_requirements"] = requirements
389
  st.session_state["generated_user_stories"] = user_stories
390
  st.session_state["requirements_generated"] = requirements is not None
391
  st.session_state["user_stories_generated"] = user_stories is not None
392
+ logger.info("Initial graph run finished (interrupted). Artifacts stored in session state.")
 
 
 
 
 
 
 
393
 
394
  @log_entry_exit
395
  def _save_artifact(self, content: str, filename: str):
 
401
  logger.info(f"Generated download link for {filename}")
402
  except Exception as e:
403
  logger.error(f"Error creating download link for {filename}: {e}")
404
+ st.error(f"Could not generate download link for {filename}.")