kamaleswar Mohanta commited on
Commit
1dd50d7
·
1 Parent(s): 14ac8e9

Add SDLC graph builder and integrate SDLC node; enhance state management with generated requirements and user stories

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/__pycache__/graph_builder_sdlc.cpython-312.pyc ADDED
Binary file (1.94 kB). View file
 
src/langgraphagenticai/graph/graph_builder.py CHANGED
@@ -4,6 +4,7 @@ from langgraph.checkpoint.memory import MemorySaver
4
  from src.langgraphagenticai.graph.graph_builder_blog import BlogGraphBuilder
5
  from src.langgraphagenticai.graph.graph_builder_basic import BasicChatbotGraphBuilder
6
  from src.langgraphagenticai.graph.graph_bulider_tool import ChatbotWithToolGraphBuilder
 
7
 
8
 
9
 
@@ -15,6 +16,7 @@ class GraphBuilder:
15
  self.blog_builder = BlogGraphBuilder(self.llm, self.memory)
16
  self.basic_builder = BasicChatbotGraphBuilder(self.llm, self.memory)
17
  self.tool_builder = ChatbotWithToolGraphBuilder(self.llm, self.memory)
 
18
 
19
  def validate_and_standardize_structure(self, user_input: str) -> list:
20
  """
 
4
  from src.langgraphagenticai.graph.graph_builder_blog import BlogGraphBuilder
5
  from src.langgraphagenticai.graph.graph_builder_basic import BasicChatbotGraphBuilder
6
  from src.langgraphagenticai.graph.graph_bulider_tool import ChatbotWithToolGraphBuilder
7
+ from src.langgraphagenticai.graph.graph_builder_sdlc import SdlcGraphBuilder
8
 
9
 
10
 
 
16
  self.blog_builder = BlogGraphBuilder(self.llm, self.memory)
17
  self.basic_builder = BasicChatbotGraphBuilder(self.llm, self.memory)
18
  self.tool_builder = ChatbotWithToolGraphBuilder(self.llm, self.memory)
19
+ self.sdlc_builder = SdlcGraphBuilder(self.llm, self.memory)
20
 
21
  def validate_and_standardize_structure(self, user_input: str) -> list:
22
  """
src/langgraphagenticai/graph/graph_builder_sdlc.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/langgraphagenticai/graph/graph_builder_blog.py
2
+ from langgraph.graph import StateGraph, START, END
3
+ from src.langgraphagenticai.nodes.sdlc_node import SdlcNode
4
+ from src.langgraphagenticai.state.state import SDLCStages, SDLCState as State
5
+ from langgraph.checkpoint.memory import MemorySaver
6
+ from pydantic import BaseModel, Field
7
+ from langchain_core.messages import SystemMessage, HumanMessage
8
+ import logging
9
+ import json
10
+ import logging
11
+ import functools
12
+ import time
13
+ from src.langgraphagenticai.logging.logging_utils import logger, log_entry_exit
14
+
15
+
16
+
17
+ class SdlcGraphBuilder:
18
+ def __init__(self, llm, memory: MemorySaver=None):
19
+ self.llm = llm
20
+ self.memory = memory if memory is not None else MemorySaver()
21
+
22
+
23
+
24
+ @log_entry_exit
25
+ def build_graph(self):
26
+ """
27
+ Builds a graph for the Software Development Life Cycle (SDLC) process.
28
+ Focuses on reliable checkpointing by adjusting interrupt timing.
29
+ """
30
+ try:
31
+ if not self.llm:
32
+ raise ValueError("LLM model not initialized")
33
+ pass
34
+ # graph_builder = StateGraph(state_schema=State)
35
+ # blog_node = BlogGenerationNode(self.llm)
36
+
37
+ # Add nodes
38
+
39
+
40
+ return # Changed from interrupt_after
41
+ except Exception as e:
42
+ logger.error(f"Error building graph: {e}")
43
+ raise
src/langgraphagenticai/nodes/sdlc_node.py CHANGED
@@ -1,6 +1,5 @@
1
  from langgraph.graph import StateGraph, START, END
2
- from langgraph.constants import Send
3
- from src.langgraphagenticai.state.state import # Import from state.py
4
  from langchain_core.messages import SystemMessage, HumanMessage
5
  import streamlit as st
6
  import json
@@ -13,358 +12,49 @@ import functools
13
  import time
14
 
15
 
16
- class BlogGenerationNode:
17
  def __init__(self, model):
18
  """Initialize the BlogGenerationNode with an LLM."""
19
  self.llm = model
20
- self.planner = model.with_structured_output(Sections)
21
 
22
- @log_entry_exit
23
- def validate_and_standardize_structure(self, user_input: str) -> List[str]:
24
- """
25
- Uses an LLM to interpret user input and generate a standardized list of blog section names.
26
- Ensures the user's specified structure is respected if provided.
27
-
28
- Args:
29
- user_input (str): The full user input from the Streamlit form (e.g., "Topic: AI\nStructure: Intro, Benefits, Summary").
30
-
31
- Returns:
32
- List[str]: A list of standardized section names (e.g., ["Intro", "Benefits", "Summary"]).
33
- """
34
- # Default structure if all else fails
35
- default_structure = ["Introduction", "Main Content", "Conclusion"]
36
-
37
- # If input is empty or whitespace-only, return default
38
- if not user_input or not user_input.strip():
39
- logger.info("Empty or whitespace-only input; returning default structure")
40
- return default_structure
41
-
42
- # Extract the user's structure if provided
43
- user_structure = None
44
- for line in user_input.split("\n"):
45
- if line.lower().startswith("structure:"):
46
- user_structure = line.split(":", 1)[1].strip()
47
- break
48
-
49
- if not user_structure:
50
- logger.info("No structure provided; returning default structure")
51
- return default_structure
52
-
53
- # Define the prompt for the LLM
54
- system_prompt = (
55
- "You are an expert blog planner. Your task is to analyze the user's input and extract or infer a clear, concise structure "
56
- "for a blog post as a list of section names. The input may explicitly list sections (e.g., 'Structure: Intro, Benefits, Summary') "
57
- "or describe them implicitly (e.g., 'I want an intro, some benefits, and a conclusion'). "
58
- "If the user provides a 'Structure' field (e.g., 'Structure: Intro, Benefits, Summary'), you MUST use those exact section names "
59
- "without modification, except for capitalizing the first letter of each section. "
60
- "If no structure is provided or it's unclear, propose a logical default structure based on the topic or context. "
61
- "Return the result as a JSON object with a single key 'sections' containing the list of section names. "
62
- "Capitalize each section name and avoid adding unnecessary sections beyond what’s indicated."
63
- )
64
-
65
- # Prepare messages for the LLM
66
- messages = [
67
- SystemMessage(content=system_prompt),
68
- HumanMessage(content=f"User input: {user_input}")
69
- ]
70
-
71
- try:
72
- # Invoke the LLM and expect a JSON response
73
- response = self.llm.invoke(messages)
74
- response_content = response.content if hasattr(response, "content") else str(response)
75
- logger.info(f"LLM response for structure: {response_content}")
76
-
77
- # Parse the JSON response
78
- result = json.loads(response_content)
79
- sections = result.get("sections", default_structure)
80
-
81
- # Validate and standardize the output
82
- if not isinstance(sections, list) or not sections:
83
- logger.warning("LLM returned invalid sections; using default structure")
84
- return default_structure
85
-
86
- # Clean up section names: strip whitespace, capitalize, remove empty strings
87
- cleaned_sections = [s.strip().capitalize() for s in sections if s.strip()]
88
-
89
- # If user provided a structure, enforce it
90
- if user_structure:
91
- user_sections = [s.strip().capitalize() for s in user_structure.split(",") if s.strip()]
92
- if len(cleaned_sections) == len(user_sections):
93
- # Override LLM sections with user sections if lengths match
94
- cleaned_sections = user_sections
95
- else:
96
- logger.warning(f"LLM section count ({len(cleaned_sections)}) doesn't match user section count ({len(user_sections)}); using user structure")
97
- cleaned_sections = user_sections
98
-
99
- return cleaned_sections if cleaned_sections else default_structure
100
-
101
- except Exception as e:
102
- logger.error(f"Error in LLM structure generation: {e}")
103
- return default_structure
104
-
105
  @log_entry_exit
106
  def user_input(self, state: State) -> dict:
107
  """Handle user input, distinguishing between initial requirements and feedback."""
108
  logger.info(f"Executing user_input with state: {state}")
109
-
110
- # Initialize requirements with existing state values to preserve them
111
- requirements = {
112
- "topic": state.get("topic", "No topic provided"),
113
- "objective": state.get("objective", "Informative"),
114
- "target_audience": state.get("target_audience", "General Audience"),
115
- "tone_style": state.get("tone_style", "Casual"),
116
- "word_count": state.get("word_count", 1000),
117
- "structure": state.get("structure", "Introduction, Main Content, Conclusion"),
118
- "feedback": state.get("feedback", "No feedback provided yet."),
119
- # Always reset these values to ensure old content doesn't persist
120
- "initial_draft": "",
121
- "completed_sections": []
122
- }
123
-
124
- # Get the latest message
125
- user_message = state["messages"][-1].content if state["messages"] else ""
126
- if not user_message:
127
- logger.warning("No user message provided; returning existing requirements with reset content")
128
- return requirements
129
-
130
- # Flag to track if the message is feedback
131
- is_feedback = False
132
-
133
- try:
134
- # Check if the message is feedback (JSON format)
135
- feedback_data = json.loads(user_message)
136
- if isinstance(feedback_data, dict) and "approved" in feedback_data:
137
- # This is a feedback message, update only the feedback field
138
- requirements["feedback"] = feedback_data.get("comments", "No feedback provided.")
139
- is_feedback = True
140
- logger.info(f"Processed feedback message: {requirements['feedback']}")
141
-
142
- # For feedback, we definitely want to ensure content reset
143
- requirements["initial_draft"] = ""
144
- requirements["completed_sections"] = []
145
- else:
146
- # Treat as requirements input
147
- temp_requirements = {}
148
- for line in user_message.split("\n"):
149
- if ": " in line:
150
- key, value = line.split(": ", 1)
151
- temp_requirements[key.lower().replace(" & ", "_").replace(" ", "_")] = value
152
-
153
- # Update requirements only for provided fields
154
- requirements.update({
155
- "topic": temp_requirements.get("topic", requirements["topic"]),
156
- "objective": temp_requirements.get("objective", requirements["objective"]),
157
- "target_audience": temp_requirements.get("target_audience", requirements["target_audience"]),
158
- "tone_style": temp_requirements.get("tone_style", requirements["tone_style"]),
159
- "word_count": int(temp_requirements.get("word_count", requirements["word_count"])),
160
- "structure": temp_requirements.get("structure", requirements["structure"]),
161
- "feedback": temp_requirements.get("feedback", requirements["feedback"]),
162
- # Always reset content for new requirements
163
- "initial_draft": "",
164
- "completed_sections": []
165
- })
166
- logger.info(f"Processed requirements input: {requirements}")
167
- except Exception as e:
168
- logger.error(f"Unexpected error processing user message: {e}")
169
- # Return existing requirements to avoid crashing, but still clear content
170
- requirements["initial_draft"] = ""
171
- requirements["completed_sections"] = []
172
- return requirements
173
-
174
- structure_input = requirements["structure"] if is_feedback else user_message
175
- standardized_structure = self.validate_and_standardize_structure(structure_input)
176
- requirements["structure"] = ", ".join(standardized_structure)
177
-
178
- # Log the final state that will be returned
179
- logger.info(f"Final parsed requirements with reset content: {requirements}")
180
- logger.info(f"Completed sections (should be empty): {requirements['completed_sections']}")
181
- logger.info(f"Initial draft (should be empty): {requirements['initial_draft']}")
182
-
183
- return requirements
184
 
185
-
186
- @log_entry_exit
187
- def orchestrator(self, state: State) -> dict:
188
- logger.info(f"Executing orchestrator with state: {state}")
189
- needs_revision = False
190
 
191
- logger.info(f"Orchestrator received completed_sections: {state.get('completed_sections', [])}")
192
-
193
- # Initialize default return values in case of early return or exception
194
- return_state = {
195
- "sections": [],
196
- "completed_sections": [],
197
- "initial_draft": ""
198
- }
199
- needs_revision=False
200
-
201
- if state.get("messages"):
202
- last_message_content = state["messages"][-1].content
203
- try:
204
- feedback_data = json.loads(last_message_content)
205
- if isinstance(feedback_data, dict) and feedback_data.get("approved") is False:
206
- needs_revision = True
207
- except json.JSONDecodeError:
208
- pass
209
- except Exception as e:
210
- logger.warning(f"Error checking last message for revision trigger: {e}")
211
-
212
- if needs_revision:
213
- logger.info("Orchestrator identified revision cycle: Clearing completed_sections.")
214
- # Don't modify state directly, include this in return dictionary instead
215
- return_state["completed_sections"] = []
216
-
217
- structure_list = [s.strip() for s in state["structure"].split(",")]
218
- section_count = len(structure_list)
219
- feedback = state.get("feedback", "No feedback provided yet.")
220
-
221
- prompt = (
222
- f"Create a detailed and structured plan for a blog report consisting of exactly {section_count} sections. "
223
- f"The content should be directly relevant to the topic: '{state['topic']}'. "
224
- f"The primary objective of the blog is to {state['objective']}, targeting an audience of {state['target_audience']}. "
225
- f"Please maintain a {state['tone_style']} tone throughout the writing. "
226
- f"Aim for a total word count of approximately {state['word_count']} words. "
227
- f"Follow this specific structure and section names: {', '.join(structure_list)}. "
228
- f"Incorporate {feedback} to enhance the quality of the content. "
229
- f"Please refrain from adding any extra sections or altering the section names unless {feedback} is provided."
230
- )
231
 
232
- try:
233
- report_sections = self.planner.invoke([
234
- SystemMessage(content=prompt),
235
- HumanMessage(content=f"Topic: {state['topic']} with feedback {feedback}")
236
- ])
237
- return_state["sections"] = report_sections.sections
238
-
239
- except Exception as e:
240
- logger.error(f"Error generating plan with LLM: {e}")
241
- # Keep the default empty values in return_state
242
-
243
- logger.info(f"Orchestrator returning: {return_state}")
244
- return return_state
245
  @log_entry_exit
246
- def llm_call(self, state: State) -> dict:
247
- """Worker writes a section of the report."""
248
- section = self.llm.invoke([
249
- SystemMessage(content="Write a report section following the provided name and description. Include no preamble for each section. Use markdown formatting."),
250
- HumanMessage(content=f"Here is the section name: {state['section'].name} and description: {state['section'].description}")
251
- ])
252
- logger.info(f"\n{'='*20}:llm_call output:{'='*20}\nGenerated section: {section.content}\n{'='*20}\n")
253
- logger.info(f"\n---------------------state[completed_sections]:---------------------------- \n{state.get('completed_sections', [])}")
254
-
255
- return {"completed_sections": state.get("completed_sections", []) + [section.content]}
256
-
257
- @log_entry_exit
258
- def synthesizer(self, state: State) -> dict:
259
- """Synthesize full report from sections and clear the sections list."""
260
- # Safely get the list, defaulting to empty if it's None or missing
261
- completed_sections = state.get("completed_sections", [])
262
-
263
- # Handle case where synthesizer might be called unexpectedly with no sections
264
- if not completed_sections:
265
- logger.warning("Synthesizer called but 'completed_sections' is empty or None.")
266
- # Return an empty draft and ensure the sections list is cleared in the state
267
- return {"initial_draft": "", "completed_sections": []}
268
-
269
- # Determine the expected number of sections based on the current plan
270
- expected_section_count = len(state.get("sections", []))
271
-
272
- # If we received more sections than expected (likely due to revision state issue),
273
- # take only the last 'expected_section_count' sections.
274
- if expected_section_count > 0 and len(completed_sections) > expected_section_count:
275
- logger.warning(f"Synthesizer received {len(completed_sections)} sections, "
276
- f"but expected {expected_section_count}. Using the last {expected_section_count}.")
277
- sections_to_use = completed_sections[-expected_section_count:]
278
- else:
279
- # Otherwise, use all received sections (normal first run or correct state)
280
- sections_to_use = completed_sections
281
-
282
- logger.info(f"Synthesizing report with sections: {completed_sections}")
283
-
284
- logger.info(f"Synthesizing report with {len(sections_to_use)} sections:")
285
- logger.info("SYNTHESIZER DEBUG:")
286
- logger.info(f"completed_sections count: {len(completed_sections)}")
287
- for i, section in enumerate(sections_to_use):
288
- # Log only the first few characters to avoid overly long logs
289
- logger.info(f"Section {i+1} (start): {section[:100]}...")
290
- logger.info(f"{'='*20}")
291
-
292
-
293
- # Join the selected sections to create the draft
294
- initial_draft = "\n\n---\n\n".join(sections_to_use)
295
- logger.info(f"Synthesized report draft generated (length: {len(initial_draft)}).")
296
-
297
- # Return the generated draft AND explicitly return an empty list
298
- # for completed_sections to update the state, clearing the old sections.
299
- return {
300
- "initial_draft": initial_draft,
301
- "completed_sections": [] # Explicitly clear the list in the returned state update
302
- }
303
-
304
- @log_entry_exit
305
- def feedback_collector(self, state: State) -> dict:
306
- logger.info(f"\n\n----------------:Entered feedback_collector with state:----------------------\n\n{state}")
307
- logger.info(f"Message count: {len(state.get('messages', []))}")
308
- logger.info(f"Last message type: {type(state['messages'][-1]) if state.get('messages') else 'None'}")
309
-
310
- if state.get("messages") and len(state["messages"]) > 0 and isinstance(state["messages"][-1], HumanMessage):
311
- try:
312
- feedback_data = json.loads(state["messages"][-1].content)
313
- is_approved = feedback_data.get("approved", False)
314
- comments = feedback_data.get("comments", "")
315
- logger.info(f"Parsed feedback: approved={is_approved}, comments={comments}")
316
-
317
- if is_approved:
318
- logger.info("Content approved, preparing final report")
319
- final_report = state.get("initial_draft", "")
320
- collector_output = {
321
- "feedback": comments,
322
- "draft_approved": True,
323
- "final_report": final_report
324
- }
325
- else:
326
- collector_output = {
327
- "feedback": comments,
328
- "draft_approved": False,
329
- "final_report": ""
330
- }
331
- logger.info(f"{'='*20}:feedback_collector output:{'='*20}\n{collector_output}") # Add this log
332
- return collector_output
333
-
334
- except json.JSONDecodeError:
335
- logger.warning("Invalid feedback format; returning default values")
336
- return {"feedback": "", "draft_approved": False, "final_report": ""}
337
 
338
- logger.info("No new feedback message found; returning default values")
339
- return {"feedback": "", "draft_approved": False, "final_report": ""}
340
  @log_entry_exit
341
- def file_generator(self, state: State) -> dict:
342
- """Generates the final report and ends the process."""
343
- final_report = state["final_report"]
344
- # In a real scenario, you would save this to a file
345
- logger.info(f"Final Report Generated:\n{final_report}")
346
- return {"final_report_path": "report.md"} # Simulate saving to a file
347
-
348
- @log_entry_exit # Conditional edge function to create llm_call workers
349
- def assign_workers(self, state: State):
350
- """Assign a worker to each section in the plan."""
351
- logger.info(f"\n{'='*10} State before assigning workers {'='*10}")
352
- logger.info(f" Current sections plan: {len(state.get('sections', []))} sections")
353
- # Log the completed_sections list specifically
354
- logger.info(f" Completed Sections before dispatch: {state.get('completed_sections', [])}")
355
- logger.info(f"{'='*40}\n")
356
- return [Send("llm_call", {"section": s}) for s in state["sections"]]
357
-
358
- @log_entry_exit# Conditional edge for feedback loop
359
- def route_feedback(self, state: State):
360
- """Route based on whether draft is approved."""
361
- draft_approved = state.get('draft_approved', False)
362
- logger.info(f"route_feedback: draft_approved = {draft_approved}")
363
-
364
- if draft_approved is True: # Strict comparison
365
- logger.info("Draft approved; routing to file_generator")
366
- return "file_generator"
367
  else:
368
- logger.info("Draft not approved; routing back to orchestrator for revision")
369
- return "orchestrator"
370
-
 
1
  from langgraph.graph import StateGraph, START, END
2
+ from src.langgraphagenticai.state.state import SDLCStages, SDLCState as State
 
3
  from langchain_core.messages import SystemMessage, HumanMessage
4
  import streamlit as st
5
  import json
 
12
  import time
13
 
14
 
15
+ class SdlcNode:
16
  def __init__(self, model):
17
  """Initialize the BlogGenerationNode with an LLM."""
18
  self.llm = model
 
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  @log_entry_exit
21
  def user_input(self, state: State) -> dict:
22
  """Handle user input, distinguishing between initial requirements and feedback."""
23
  logger.info(f"Executing user_input with state: {state}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ state.project_name = st.session_state.get("project_name")
26
+ state.project_description = st.session_state.get("project_description")
27
+ state.project_goals = st.session_state.get("project_goals")
28
+ state.project_scope = st.session_state.get("project_scope")
29
+ state.project_objectives = st.session_state.get("project_objectives")
30
 
31
+ return {"user_input": "captured"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  @log_entry_exit
34
+ def generate_requirements(self, state: State) -> dict:
35
+ """Generate requirements based on user input."""
36
+ logger.info(f"Generating requirements with state: {state}")
37
+
38
+ requirements_input = {
39
+ "project_name": state.project_name if state.project_name is not None else "No project name provided",
40
+ "project_description": state.project_description if state.project_description is not None else "No project description provided",
41
+ "project_goals": state.project_goals if state.project_goals is not None else "No project goals provided",
42
+ "project_scope": state.project_scope if state.project_scope is not None else "No project scope provided",
43
+ "project_objectives": state.project_objectives if state.project_objectives is not None else "No project objectives provided",
44
+ }
45
+ prompt = f"Generate detailed requirements for the following project details:\n{json.dumps(requirements_input, indent=2)}"
46
+ state.generated_requirements = self.llm(prompt)
47
+ return {"generated_requirements": state.generated_requirements}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
 
 
49
  @log_entry_exit
50
+ def generate_user_stories(self, state: State) -> dict:
51
+ """Generate user stories based on the requirements."""
52
+ logger.info(f"Generating user stories with state: {state}")
53
+
54
+ if state.generated_requirements:
55
+ prompt = f"Generate user stories based on the following requirements:\n{state.generated_requirements}"
56
+ state.user_stories = self.llm(prompt)
57
+ return {"user_stories": state.user_stories}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  else:
59
+ state.user_stories = "No requirements generated yet."
60
+ return {"user_stories": state.user_stories}
 
src/langgraphagenticai/state/state.py CHANGED
@@ -59,6 +59,7 @@ class SDLCState(BaseModel):
59
  This class represents the state of a software development project, including the current stage, inputs, artifacts, and feedback.
60
 
61
  eg:
 
62
  1. Project Description:
63
 
64
  Definition: A concise and high-level summary of what the project is about. It provides a general understanding of the project's purpose and nature.
@@ -126,6 +127,8 @@ The objectives provide specific, measurable steps to achieve the goals within th
126
  project_scope: Optional[str] = Field(None, description="Scope of the project.")
127
  project_objectives: Optional[str] = Field(None, description="Objectives of the project.")
128
  requirements: Optional[str] = Field(None, description="Detailed project requirements.")
 
 
129
 
130
  # Artifacts generated during different SDLC stages
131
  planning_artifact: Optional[str] = Field(None, description="Artifact generated during the planning stage.")
 
59
  This class represents the state of a software development project, including the current stage, inputs, artifacts, and feedback.
60
 
61
  eg:
62
+ Requirements are the foundation of any project. They provide a clear understanding of what needs to be built and why. In the context of software development, requirements are typically categorized into four main types:
63
  1. Project Description:
64
 
65
  Definition: A concise and high-level summary of what the project is about. It provides a general understanding of the project's purpose and nature.
 
127
  project_scope: Optional[str] = Field(None, description="Scope of the project.")
128
  project_objectives: Optional[str] = Field(None, description="Objectives of the project.")
129
  requirements: Optional[str] = Field(None, description="Detailed project requirements.")
130
+ generated_requirements: Optional[str] = Field(None, description="Generated requirements based on user input.")
131
+ user_stories: Optional[str] = Field(None, description="User stories generated based on requirements.")
132
 
133
  # Artifacts generated during different SDLC stages
134
  planning_artifact: Optional[str] = Field(None, description="Artifact generated during the planning stage.")