Rakshitjan commited on
Commit
650a3ba
·
verified ·
1 Parent(s): 8cc8098

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +429 -430
app.py CHANGED
@@ -1,431 +1,430 @@
1
- import json
2
- import operator
3
- import os
4
- import getpass
5
- from langchain_anthropic import ChatAnthropic
6
- from langchain_openai import ChatOpenAI
7
- from typing import Annotated, List
8
- from pydantic import BaseModel, Field
9
- from typing_extensions import TypedDict
10
- from langgraph.graph import StateGraph, START, END
11
- from IPython.display import Image, display
12
- from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
13
- from langgraph.graph import MessagesState
14
- from typing_extensions import Literal
15
- from langchain_core.tools import tool
16
- import json
17
- from datetime import datetime, timedelta
18
- import streamlit as st
19
-
20
- st.set_page_config(layout="wide")
21
-
22
- st.title("RoadMap Test")
23
-
24
-
25
- # # setting LLM
26
- # def _set_env(var: str):
27
- # if not os.environ.get(var):
28
- # os.environ[var] = getpass.getpass(f"{var}: ")
29
-
30
-
31
- # _set_env("ANTHROPIC_API_KEY")
32
-
33
- os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
34
-
35
- llm = ChatOpenAI(model="gpt-4o-mini")
36
-
37
-
38
- with open('fourdayRoadmap.json', 'r') as file:
39
- data = json.load(file) # Load JSON content into a Python dictionary -->display 1
40
-
41
-
42
- data_old = data # Added for stramlit app,not the part of actual code
43
-
44
- #Intermediate Step : Add variables task_completed as false and completion_timestamp as null
45
- for day in data["schedule"]:
46
- for subject in day["subjects"]:
47
- for task in subject["tasks"]:
48
- task["task_completed"] = False
49
- task["completion_timestamp"] = None
50
-
51
- # Step 2 : Extract The previous day from the extracted roadmap
52
- previous_day = data["schedule"][0]
53
- previous_day_roadmap_str = str(previous_day)
54
-
55
- #Step 3 : Extract the incompleted tasks
56
- # Extract incomplete tasks
57
- incomplete_tasks = {
58
- "dayNumber": previous_day["dayNumber"],
59
- "date": previous_day["date"],
60
- "subjects": []
61
- }
62
-
63
- for subject in previous_day["subjects"]:
64
- incomplete_subject_tasks = [
65
- {
66
- "ChapterName": task["ChapterName"],
67
- "type": task["type"],
68
- "subtopic": task["subtopic"],
69
- "time": task["time"],
70
- "task_completed": False,
71
- "completion_timestamp": None
72
- }
73
- for task in subject["tasks"] if not task["task_completed"]
74
- ]
75
-
76
- if incomplete_subject_tasks:
77
- incomplete_tasks["subjects"].append({
78
- "name": subject["name"],
79
- "tasks": incomplete_subject_tasks
80
- })
81
-
82
- # Convert to JSON format
83
- incomplete_tasks_json = json.dumps(incomplete_tasks, indent=4)
84
-
85
- #Generate a incomplete_tasks_list for the the agent 2
86
- # Extract incomplete tasks
87
- incomplete_task_list = []
88
- incomplete_task_list_json = json.loads(incomplete_tasks_json)
89
- for subject in incomplete_task_list_json["subjects"]:
90
- for task in subject["tasks"]:
91
- if not task["task_completed"]:
92
- incomplete_task = {
93
- "subject": subject["name"],
94
- "ChapterName": task["ChapterName"],
95
- "type": task["type"],
96
- "subtopic": task["subtopic"],
97
- "time": task["time"],
98
- "task_completed": task["task_completed"],
99
- "completion_timestamp": task["completion_timestamp"]
100
- }
101
- incomplete_task_list.append(incomplete_task)
102
-
103
- #Now we have to make an Orchestrator - worker workflow which generates a report on the roadmap data and defining sections which are most relevant and so giving a report
104
-
105
-
106
- # Schema for structured output to use in planning
107
-
108
- class Section(BaseModel): # This part defines the structure of a single section of the report, saying that it should return a field name and a description
109
- name: str = Field(
110
- description="Name for this section of the report.",
111
- )
112
- description: str = Field(
113
- description="Brief overview of the main topics and concepts to be covered in this section.",
114
- )
115
-
116
-
117
- class Sections(BaseModel): # This part defines what has to be the output of the llm
118
- sections: List[Section] = Field(
119
- description="Sections of the report.",
120
- )
121
-
122
-
123
- # Augment the LLM with schema for structured output
124
- planner = llm.with_structured_output(Sections)
125
-
126
- from langgraph.constants import Send
127
-
128
- # This is a state which defines all params that we need to collect in this WF
129
- # Graph state
130
- class State(TypedDict):
131
- previous_day_roadmap: str # Report topic
132
- sections: list[Section] # List of report sections
133
- completed_sections: Annotated[
134
- list, operator.add
135
- ] # All workers write to this key in parallel
136
- final_report: str # Final report
137
-
138
-
139
- # Worker state
140
- class WorkerState(TypedDict):
141
- section: Section
142
- completed_sections: Annotated[list, operator.add]
143
-
144
-
145
- # Nodes
146
- def orchestrator(state: State):
147
- """Orchestrator that generates a plan for the report"""
148
-
149
- # Generate queries
150
- report_sections = planner.invoke(
151
- [
152
- SystemMessage(content="""Generate a plan for the report. The report is a review and analysis output, which tells us about
153
- the study pattern, studying hours, non studying hours and future action plan for the user based on
154
- performance of task completion on a day of the user. We only want to understand about how the student studies nothing apart from that
155
- Dont halucinate the data, just keep focus on provided data, no need for data out of the box.
156
- I want a very short report to understand how many tasks student completed and then what is the studying pattern of the user
157
- """),
158
- HumanMessage(content=f"Here is the Previous day roadmap of the user: {state['previous_day_roadmap']}"),
159
- ]
160
- )
161
-
162
- return {"sections": report_sections.sections}
163
-
164
-
165
- def llm_call(state: WorkerState):
166
- """Worker writes a section of the report"""
167
-
168
- # Generate section
169
- section = llm.invoke(
170
- [
171
- SystemMessage(
172
- content="""Write a report section following the provided name and description. Make this report userful for parents, teachers
173
- and students himself, also try to motivate the student.This report is for a Joint Entrance Examination aspirant.
174
- Keep the report very short but will all clear parameter and make sure the report is not so big but contains all the necessary
175
- details of the student's day. Keep the report very short and avoid texts and focus on numbers
176
- Include no preamble for each section.
177
- Use markdown formatting."""
178
- ),
179
- HumanMessage(
180
- content=f"Here is the section name: {state['section'].name} and description: {state['section'].description}"
181
- ),
182
- ]
183
- )
184
-
185
- # Write the updated section to completed sections
186
- return {"completed_sections": [section.content]}
187
-
188
-
189
- def synthesizer(state: State):
190
- """Synthesize full report from sections"""
191
-
192
- # List of completed sections
193
- completed_sections = state["completed_sections"]
194
-
195
- # Format completed section to str to use as context for final sections
196
- completed_report_sections = "\n\n---\n\n".join(completed_sections)
197
-
198
- return {"final_report": completed_report_sections}
199
-
200
-
201
- # Conditional edge function to create llm_call workers that each write a section of the report
202
- def assign_workers(state: State):
203
- """Assign a worker to each section in the plan"""
204
-
205
- # Kick off section writing in parallel via Send() API
206
- return [Send("llm_call", {"section": s}) for s in state["sections"]]
207
-
208
-
209
- # Build workflow
210
- orchestrator_worker_builder = StateGraph(State)
211
-
212
- # Add the nodes
213
- orchestrator_worker_builder.add_node("orchestrator", orchestrator)
214
- orchestrator_worker_builder.add_node("llm_call", llm_call)
215
- orchestrator_worker_builder.add_node("synthesizer", synthesizer)
216
-
217
- # Add edges to connect nodes
218
- orchestrator_worker_builder.add_edge(START, "orchestrator")
219
- orchestrator_worker_builder.add_conditional_edges(
220
- "orchestrator", assign_workers, ["llm_call"]
221
- )
222
- orchestrator_worker_builder.add_edge("llm_call", "synthesizer")
223
- orchestrator_worker_builder.add_edge("synthesizer", END)
224
-
225
- # Compile the workflow
226
- orchestrator_worker = orchestrator_worker_builder.compile()
227
-
228
- # # Show the workflow
229
- # display(Image(orchestrator_worker.get_graph().draw_mermaid_png()))
230
-
231
- # Invoke
232
- state = orchestrator_worker.invoke({"previous_day_roadmap": f"{previous_day_roadmap_str}"})
233
-
234
- from IPython.display import Markdown
235
- final_report = state["final_report"] #-->display 2
236
-
237
- #Evaluator-optimizer approach
238
- def remove_the_first_day(roadmap):
239
- new_roadmap={
240
- "schedule":[
241
-
242
- ]
243
- }
244
- for day in roadmap['schedule']:
245
- if day['dayNumber']!=1:
246
- new_roadmap['schedule'].append(day)
247
- return new_roadmap
248
-
249
- roadmap = remove_the_first_day(data)
250
-
251
- available_dates = []
252
- for day in roadmap['schedule']:
253
- available_dates.append(day['date'])
254
- print(available_dates)
255
-
256
- # Graph state
257
- class State(TypedDict):
258
- roadmap: dict
259
- student_report: str
260
- available_dates: dict
261
- incomplete_task_list: list
262
- feedback: str
263
- added_or_not: str
264
-
265
-
266
- # Schema for structured output to use in evaluation
267
- class Feedback(BaseModel):
268
- grade: Literal["added", "not added"] = Field(
269
- description="Check if all the incomplete tasks are added to the roadmap or not",
270
- )
271
- feedback: str = Field(
272
- description="If some tasks are not not added, give feedback to add those tasks also",
273
- )
274
-
275
-
276
- # Augment the LLM with schema for structured output
277
- evaluator = llm.with_structured_output(Feedback)
278
-
279
-
280
- # Nodes
281
- def llm_call_generator(state: State):
282
- """The function which addes the roadmap and incomplete tasks list to the state"""
283
- roadmap_str = """
284
- {
285
- 'roadmap':[
286
- 'date':'YYYY-MM-DD',
287
- 'tasks':[
288
- <List of tasks>
289
- ]
290
- ]
291
- }
292
- """
293
- if state.get("feedback"):
294
- msg = llm.invoke(
295
- f"""Add the following incomplete_tasks {state['incomplete_task_list']} to the roadmap key and take into account the feedback {state['feedback']}
296
- and make sure that we dyanamically add the tasks not increasing the load on just one day, also we have to add the tasks on
297
- following dates only {state['available_dates']} only also we need to focus on student's performance report while adding tasks {state['student_report']}
298
- Make sure you only give roadmap json as output and nothing else, strictly follow the output structure {roadmap_str}
299
- """
300
- )
301
- else:
302
- ## In this case we have to just add the roadmap and incomplete tasks list to the state
303
- msg = llm.invoke(f"""Add the following incomplete_tasks {state['incomplete_task_list']} to the roadmap key
304
- and make sure that we dyanamically add the tasks not increasing the load on just one day, also we have to add the tasks on
305
- following dates only {state['available_dates']} only also we need to focus on student's performance report while adding tasks {state['student_report']}\
306
- Make sure you only give roadmap json as output and nothing else, strictly follow the output structure {roadmap_str}
307
- """)
308
- return {"roadmap": msg.content}
309
-
310
-
311
- def llm_call_evaluator(state: State):
312
- """This the evaluator that checks where all the tasks are added to the roadmap or not"""
313
- grade = evaluator.invoke(f"Grade the roadmap {state['roadmap']} by checking the {state['incomplete_task_list']} as all added or not and make sure that we dyanamically add the tasks not increasing the load on just one day, also we need to make sure that we return an roadmap with proper json format as the roadmap was")
314
- return {"added_or_not": grade.grade, "feedback": grade.feedback}
315
-
316
-
317
- # Conditional edge function to route back to joke generator or end based upon feedback from the evaluator
318
- def route_roadmap(state: State):
319
- """Route back to the generator if roadmap does not have all the incomplete tasks"""
320
- if state["added_or_not"] == "added":
321
- return "Accepted"
322
- elif state["added_or_not"] == "not added":
323
- return "Rejected + Feedback"
324
-
325
-
326
- # Build workflow
327
- optimizer_builder = StateGraph(State)
328
-
329
- # Add the nodes
330
- optimizer_builder.add_node("llm_call_generator", llm_call_generator)
331
- optimizer_builder.add_node("llm_call_evaluator", llm_call_evaluator)
332
-
333
- # Add edges to connect nodes
334
- optimizer_builder.add_edge(START, "llm_call_generator")
335
- optimizer_builder.add_edge("llm_call_generator", "llm_call_evaluator")
336
- optimizer_builder.add_conditional_edges(
337
- "llm_call_evaluator",
338
- route_roadmap,
339
- { # Name returned by route_roadmap : Name of next node to visit
340
- "Accepted": END,
341
- "Rejected + Feedback": "llm_call_generator",
342
- },
343
- )
344
-
345
- # Compile the workflow
346
- optimizer_workflow = optimizer_builder.compile()
347
-
348
- # # Show the workflow
349
- # display(Image(optimizer_workflow.get_graph().draw_mermaid_png()))
350
-
351
- # Invoke
352
- state = optimizer_workflow.invoke({"student_report":final_report,"available_dates": available_dates, "incomplete_task_list":incomplete_task_list})
353
- shifted_tasks_roadmap = state["roadmap"]
354
- shifted_tasks_roadmap = shifted_tasks_roadmap.split("json")[1].split("```")[0]
355
- print(shifted_tasks_roadmap)
356
-
357
-
358
- #Now we merge the shifted roadmap
359
- def add_task(roadmap, task, date_task_to_be_added):
360
- subject_name = task["subject"] # Extract subject
361
- chapter_name = task["ChapterName"]
362
- topic_name = task["subtopic"]
363
- type_name = task["type"]
364
-
365
- # Check if the date exists
366
- for day in roadmap['schedule']:
367
- if day['date'] == date_task_to_be_added:
368
- for subject in day['subjects']:
369
- if subject['name'] == subject_name:
370
- # Check if task already exists
371
- for existing_task in subject['tasks']:
372
- if existing_task['ChapterName'] == chapter_name and existing_task['type'] == type_name and existing_task['subtopic'] == topic_name:
373
- return roadmap # Task already exists, return unchanged roadmap
374
-
375
- # Add task
376
- temp_task = {
377
- "ChapterName": chapter_name,
378
- "type": type_name,
379
- "topic": topic_name,
380
- "time": task["time"],
381
- "task_completed": False,
382
- "completion_timestamp": None
383
- }
384
- subject['tasks'].append(temp_task)
385
- return roadmap # Return updated roadmap
386
-
387
- for day in json.loads(shifted_tasks_roadmap)["roadmap"]:
388
- date = day['date']
389
- tasks = day['tasks']
390
- for task in tasks:
391
- add_task(data,task,date)
392
-
393
-
394
-
395
-
396
- #stramlit section
397
-
398
- # Initialize session state for page navigation if it doesn't exist
399
- if "page" not in st.session_state:
400
- st.session_state["page"] = 1
401
-
402
- def show_page_1():
403
- st.title("Roadmaps")
404
-
405
- col1, col2 = st.columns([1, 1])
406
-
407
- with col1:
408
- st.subheader("Original Roadmap")
409
- st.json(data_old)
410
-
411
- with col2:
412
- st.subheader("Roadmap with Shifted Task")
413
- st.json(data)
414
-
415
-
416
- def show_page_2():
417
- st.title("Report")
418
- st.markdown(final_report)
419
-
420
- # Navigation
421
- st.sidebar.title("Navigation")
422
- if st.sidebar.button("Roadmaps Page"):
423
- st.session_state["page"] = 1
424
- if st.sidebar.button("Report Page"):
425
- st.session_state["page"] = 2
426
-
427
- # Display content based on the current page
428
- if st.session_state["page"] == 1:
429
- show_page_1()
430
- elif st.session_state["page"] == 2:
431
  show_page_2()
 
1
+ import json
2
+ import operator
3
+ import os
4
+ import getpass
5
+ from langchain_openai import ChatOpenAI
6
+ from typing import Annotated, List
7
+ from pydantic import BaseModel, Field
8
+ from typing_extensions import TypedDict
9
+ from langgraph.graph import StateGraph, START, END
10
+ from IPython.display import Image, display
11
+ from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
12
+ from langgraph.graph import MessagesState
13
+ from typing_extensions import Literal
14
+ from langchain_core.tools import tool
15
+ import json
16
+ from datetime import datetime, timedelta
17
+ import streamlit as st
18
+
19
+ st.set_page_config(layout="wide")
20
+
21
+ st.title("RoadMap Test")
22
+
23
+
24
+ # # setting LLM
25
+ # def _set_env(var: str):
26
+ # if not os.environ.get(var):
27
+ # os.environ[var] = getpass.getpass(f"{var}: ")
28
+
29
+
30
+ # _set_env("ANTHROPIC_API_KEY")
31
+
32
+ os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
33
+
34
+ llm = ChatOpenAI(model="gpt-4o-mini")
35
+
36
+
37
+ with open('fourdayRoadmap.json', 'r') as file:
38
+ data = json.load(file) # Load JSON content into a Python dictionary -->display 1
39
+
40
+
41
+ data_old = data # Added for stramlit app,not the part of actual code
42
+
43
+ #Intermediate Step : Add variables task_completed as false and completion_timestamp as null
44
+ for day in data["schedule"]:
45
+ for subject in day["subjects"]:
46
+ for task in subject["tasks"]:
47
+ task["task_completed"] = False
48
+ task["completion_timestamp"] = None
49
+
50
+ # Step 2 : Extract The previous day from the extracted roadmap
51
+ previous_day = data["schedule"][0]
52
+ previous_day_roadmap_str = str(previous_day)
53
+
54
+ #Step 3 : Extract the incompleted tasks
55
+ # Extract incomplete tasks
56
+ incomplete_tasks = {
57
+ "dayNumber": previous_day["dayNumber"],
58
+ "date": previous_day["date"],
59
+ "subjects": []
60
+ }
61
+
62
+ for subject in previous_day["subjects"]:
63
+ incomplete_subject_tasks = [
64
+ {
65
+ "ChapterName": task["ChapterName"],
66
+ "type": task["type"],
67
+ "subtopic": task["subtopic"],
68
+ "time": task["time"],
69
+ "task_completed": False,
70
+ "completion_timestamp": None
71
+ }
72
+ for task in subject["tasks"] if not task["task_completed"]
73
+ ]
74
+
75
+ if incomplete_subject_tasks:
76
+ incomplete_tasks["subjects"].append({
77
+ "name": subject["name"],
78
+ "tasks": incomplete_subject_tasks
79
+ })
80
+
81
+ # Convert to JSON format
82
+ incomplete_tasks_json = json.dumps(incomplete_tasks, indent=4)
83
+
84
+ #Generate a incomplete_tasks_list for the the agent 2
85
+ # Extract incomplete tasks
86
+ incomplete_task_list = []
87
+ incomplete_task_list_json = json.loads(incomplete_tasks_json)
88
+ for subject in incomplete_task_list_json["subjects"]:
89
+ for task in subject["tasks"]:
90
+ if not task["task_completed"]:
91
+ incomplete_task = {
92
+ "subject": subject["name"],
93
+ "ChapterName": task["ChapterName"],
94
+ "type": task["type"],
95
+ "subtopic": task["subtopic"],
96
+ "time": task["time"],
97
+ "task_completed": task["task_completed"],
98
+ "completion_timestamp": task["completion_timestamp"]
99
+ }
100
+ incomplete_task_list.append(incomplete_task)
101
+
102
+ #Now we have to make an Orchestrator - worker workflow which generates a report on the roadmap data and defining sections which are most relevant and so giving a report
103
+
104
+
105
+ # Schema for structured output to use in planning
106
+
107
+ class Section(BaseModel): # This part defines the structure of a single section of the report, saying that it should return a field name and a description
108
+ name: str = Field(
109
+ description="Name for this section of the report.",
110
+ )
111
+ description: str = Field(
112
+ description="Brief overview of the main topics and concepts to be covered in this section.",
113
+ )
114
+
115
+
116
+ class Sections(BaseModel): # This part defines what has to be the output of the llm
117
+ sections: List[Section] = Field(
118
+ description="Sections of the report.",
119
+ )
120
+
121
+
122
+ # Augment the LLM with schema for structured output
123
+ planner = llm.with_structured_output(Sections)
124
+
125
+ from langgraph.constants import Send
126
+
127
+ # This is a state which defines all params that we need to collect in this WF
128
+ # Graph state
129
+ class State(TypedDict):
130
+ previous_day_roadmap: str # Report topic
131
+ sections: list[Section] # List of report sections
132
+ completed_sections: Annotated[
133
+ list, operator.add
134
+ ] # All workers write to this key in parallel
135
+ final_report: str # Final report
136
+
137
+
138
+ # Worker state
139
+ class WorkerState(TypedDict):
140
+ section: Section
141
+ completed_sections: Annotated[list, operator.add]
142
+
143
+
144
+ # Nodes
145
+ def orchestrator(state: State):
146
+ """Orchestrator that generates a plan for the report"""
147
+
148
+ # Generate queries
149
+ report_sections = planner.invoke(
150
+ [
151
+ SystemMessage(content="""Generate a plan for the report. The report is a review and analysis output, which tells us about
152
+ the study pattern, studying hours, non studying hours and future action plan for the user based on
153
+ performance of task completion on a day of the user. We only want to understand about how the student studies nothing apart from that
154
+ Dont halucinate the data, just keep focus on provided data, no need for data out of the box.
155
+ I want a very short report to understand how many tasks student completed and then what is the studying pattern of the user
156
+ """),
157
+ HumanMessage(content=f"Here is the Previous day roadmap of the user: {state['previous_day_roadmap']}"),
158
+ ]
159
+ )
160
+
161
+ return {"sections": report_sections.sections}
162
+
163
+
164
+ def llm_call(state: WorkerState):
165
+ """Worker writes a section of the report"""
166
+
167
+ # Generate section
168
+ section = llm.invoke(
169
+ [
170
+ SystemMessage(
171
+ content="""Write a report section following the provided name and description. Make this report userful for parents, teachers
172
+ and students himself, also try to motivate the student.This report is for a Joint Entrance Examination aspirant.
173
+ Keep the report very short but will all clear parameter and make sure the report is not so big but contains all the necessary
174
+ details of the student's day. Keep the report very short and avoid texts and focus on numbers
175
+ Include no preamble for each section.
176
+ Use markdown formatting."""
177
+ ),
178
+ HumanMessage(
179
+ content=f"Here is the section name: {state['section'].name} and description: {state['section'].description}"
180
+ ),
181
+ ]
182
+ )
183
+
184
+ # Write the updated section to completed sections
185
+ return {"completed_sections": [section.content]}
186
+
187
+
188
+ def synthesizer(state: State):
189
+ """Synthesize full report from sections"""
190
+
191
+ # List of completed sections
192
+ completed_sections = state["completed_sections"]
193
+
194
+ # Format completed section to str to use as context for final sections
195
+ completed_report_sections = "\n\n---\n\n".join(completed_sections)
196
+
197
+ return {"final_report": completed_report_sections}
198
+
199
+
200
+ # Conditional edge function to create llm_call workers that each write a section of the report
201
+ def assign_workers(state: State):
202
+ """Assign a worker to each section in the plan"""
203
+
204
+ # Kick off section writing in parallel via Send() API
205
+ return [Send("llm_call", {"section": s}) for s in state["sections"]]
206
+
207
+
208
+ # Build workflow
209
+ orchestrator_worker_builder = StateGraph(State)
210
+
211
+ # Add the nodes
212
+ orchestrator_worker_builder.add_node("orchestrator", orchestrator)
213
+ orchestrator_worker_builder.add_node("llm_call", llm_call)
214
+ orchestrator_worker_builder.add_node("synthesizer", synthesizer)
215
+
216
+ # Add edges to connect nodes
217
+ orchestrator_worker_builder.add_edge(START, "orchestrator")
218
+ orchestrator_worker_builder.add_conditional_edges(
219
+ "orchestrator", assign_workers, ["llm_call"]
220
+ )
221
+ orchestrator_worker_builder.add_edge("llm_call", "synthesizer")
222
+ orchestrator_worker_builder.add_edge("synthesizer", END)
223
+
224
+ # Compile the workflow
225
+ orchestrator_worker = orchestrator_worker_builder.compile()
226
+
227
+ # # Show the workflow
228
+ # display(Image(orchestrator_worker.get_graph().draw_mermaid_png()))
229
+
230
+ # Invoke
231
+ state = orchestrator_worker.invoke({"previous_day_roadmap": f"{previous_day_roadmap_str}"})
232
+
233
+ from IPython.display import Markdown
234
+ final_report = state["final_report"] #-->display 2
235
+
236
+ #Evaluator-optimizer approach
237
+ def remove_the_first_day(roadmap):
238
+ new_roadmap={
239
+ "schedule":[
240
+
241
+ ]
242
+ }
243
+ for day in roadmap['schedule']:
244
+ if day['dayNumber']!=1:
245
+ new_roadmap['schedule'].append(day)
246
+ return new_roadmap
247
+
248
+ roadmap = remove_the_first_day(data)
249
+
250
+ available_dates = []
251
+ for day in roadmap['schedule']:
252
+ available_dates.append(day['date'])
253
+ print(available_dates)
254
+
255
+ # Graph state
256
+ class State(TypedDict):
257
+ roadmap: dict
258
+ student_report: str
259
+ available_dates: dict
260
+ incomplete_task_list: list
261
+ feedback: str
262
+ added_or_not: str
263
+
264
+
265
+ # Schema for structured output to use in evaluation
266
+ class Feedback(BaseModel):
267
+ grade: Literal["added", "not added"] = Field(
268
+ description="Check if all the incomplete tasks are added to the roadmap or not",
269
+ )
270
+ feedback: str = Field(
271
+ description="If some tasks are not not added, give feedback to add those tasks also",
272
+ )
273
+
274
+
275
+ # Augment the LLM with schema for structured output
276
+ evaluator = llm.with_structured_output(Feedback)
277
+
278
+
279
+ # Nodes
280
+ def llm_call_generator(state: State):
281
+ """The function which addes the roadmap and incomplete tasks list to the state"""
282
+ roadmap_str = """
283
+ {
284
+ 'roadmap':[
285
+ 'date':'YYYY-MM-DD',
286
+ 'tasks':[
287
+ <List of tasks>
288
+ ]
289
+ ]
290
+ }
291
+ """
292
+ if state.get("feedback"):
293
+ msg = llm.invoke(
294
+ f"""Add the following incomplete_tasks {state['incomplete_task_list']} to the roadmap key and take into account the feedback {state['feedback']}
295
+ and make sure that we dyanamically add the tasks not increasing the load on just one day, also we have to add the tasks on
296
+ following dates only {state['available_dates']} only also we need to focus on student's performance report while adding tasks {state['student_report']}
297
+ Make sure you only give roadmap json as output and nothing else, strictly follow the output structure {roadmap_str}
298
+ """
299
+ )
300
+ else:
301
+ ## In this case we have to just add the roadmap and incomplete tasks list to the state
302
+ msg = llm.invoke(f"""Add the following incomplete_tasks {state['incomplete_task_list']} to the roadmap key
303
+ and make sure that we dyanamically add the tasks not increasing the load on just one day, also we have to add the tasks on
304
+ following dates only {state['available_dates']} only also we need to focus on student's performance report while adding tasks {state['student_report']}\
305
+ Make sure you only give roadmap json as output and nothing else, strictly follow the output structure {roadmap_str}
306
+ """)
307
+ return {"roadmap": msg.content}
308
+
309
+
310
+ def llm_call_evaluator(state: State):
311
+ """This the evaluator that checks where all the tasks are added to the roadmap or not"""
312
+ grade = evaluator.invoke(f"Grade the roadmap {state['roadmap']} by checking the {state['incomplete_task_list']} as all added or not and make sure that we dyanamically add the tasks not increasing the load on just one day, also we need to make sure that we return an roadmap with proper json format as the roadmap was")
313
+ return {"added_or_not": grade.grade, "feedback": grade.feedback}
314
+
315
+
316
+ # Conditional edge function to route back to joke generator or end based upon feedback from the evaluator
317
+ def route_roadmap(state: State):
318
+ """Route back to the generator if roadmap does not have all the incomplete tasks"""
319
+ if state["added_or_not"] == "added":
320
+ return "Accepted"
321
+ elif state["added_or_not"] == "not added":
322
+ return "Rejected + Feedback"
323
+
324
+
325
+ # Build workflow
326
+ optimizer_builder = StateGraph(State)
327
+
328
+ # Add the nodes
329
+ optimizer_builder.add_node("llm_call_generator", llm_call_generator)
330
+ optimizer_builder.add_node("llm_call_evaluator", llm_call_evaluator)
331
+
332
+ # Add edges to connect nodes
333
+ optimizer_builder.add_edge(START, "llm_call_generator")
334
+ optimizer_builder.add_edge("llm_call_generator", "llm_call_evaluator")
335
+ optimizer_builder.add_conditional_edges(
336
+ "llm_call_evaluator",
337
+ route_roadmap,
338
+ { # Name returned by route_roadmap : Name of next node to visit
339
+ "Accepted": END,
340
+ "Rejected + Feedback": "llm_call_generator",
341
+ },
342
+ )
343
+
344
+ # Compile the workflow
345
+ optimizer_workflow = optimizer_builder.compile()
346
+
347
+ # # Show the workflow
348
+ # display(Image(optimizer_workflow.get_graph().draw_mermaid_png()))
349
+
350
+ # Invoke
351
+ state = optimizer_workflow.invoke({"student_report":final_report,"available_dates": available_dates, "incomplete_task_list":incomplete_task_list})
352
+ shifted_tasks_roadmap = state["roadmap"]
353
+ shifted_tasks_roadmap = shifted_tasks_roadmap.split("json")[1].split("```")[0]
354
+ print(shifted_tasks_roadmap)
355
+
356
+
357
+ #Now we merge the shifted roadmap
358
+ def add_task(roadmap, task, date_task_to_be_added):
359
+ subject_name = task["subject"] # Extract subject
360
+ chapter_name = task["ChapterName"]
361
+ topic_name = task["subtopic"]
362
+ type_name = task["type"]
363
+
364
+ # Check if the date exists
365
+ for day in roadmap['schedule']:
366
+ if day['date'] == date_task_to_be_added:
367
+ for subject in day['subjects']:
368
+ if subject['name'] == subject_name:
369
+ # Check if task already exists
370
+ for existing_task in subject['tasks']:
371
+ if existing_task['ChapterName'] == chapter_name and existing_task['type'] == type_name and existing_task['subtopic'] == topic_name:
372
+ return roadmap # Task already exists, return unchanged roadmap
373
+
374
+ # Add task
375
+ temp_task = {
376
+ "ChapterName": chapter_name,
377
+ "type": type_name,
378
+ "topic": topic_name,
379
+ "time": task["time"],
380
+ "task_completed": False,
381
+ "completion_timestamp": None
382
+ }
383
+ subject['tasks'].append(temp_task)
384
+ return roadmap # Return updated roadmap
385
+
386
+ for day in json.loads(shifted_tasks_roadmap)["roadmap"]:
387
+ date = day['date']
388
+ tasks = day['tasks']
389
+ for task in tasks:
390
+ add_task(data,task,date)
391
+
392
+
393
+
394
+
395
+ #stramlit section
396
+
397
+ # Initialize session state for page navigation if it doesn't exist
398
+ if "page" not in st.session_state:
399
+ st.session_state["page"] = 1
400
+
401
+ def show_page_1():
402
+ st.title("Roadmaps")
403
+
404
+ col1, col2 = st.columns([1, 1])
405
+
406
+ with col1:
407
+ st.subheader("Original Roadmap")
408
+ st.json(data_old)
409
+
410
+ with col2:
411
+ st.subheader("Roadmap with Shifted Task")
412
+ st.json(data)
413
+
414
+
415
+ def show_page_2():
416
+ st.title("Report")
417
+ st.markdown(final_report)
418
+
419
+ # Navigation
420
+ st.sidebar.title("Navigation")
421
+ if st.sidebar.button("Roadmaps Page"):
422
+ st.session_state["page"] = 1
423
+ if st.sidebar.button("Report Page"):
424
+ st.session_state["page"] = 2
425
+
426
+ # Display content based on the current page
427
+ if st.session_state["page"] == 1:
428
+ show_page_1()
429
+ elif st.session_state["page"] == 2:
 
430
  show_page_2()