Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,431 +1,430 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import operator
|
| 3 |
-
import os
|
| 4 |
-
import getpass
|
| 5 |
-
from
|
| 6 |
-
from
|
| 7 |
-
from
|
| 8 |
-
from
|
| 9 |
-
from
|
| 10 |
-
from
|
| 11 |
-
from
|
| 12 |
-
from
|
| 13 |
-
from
|
| 14 |
-
from
|
| 15 |
-
|
| 16 |
-
import
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
#
|
| 26 |
-
#
|
| 27 |
-
#
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
for
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
task["
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
#
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
"
|
| 59 |
-
"
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
"
|
| 67 |
-
"
|
| 68 |
-
"
|
| 69 |
-
"
|
| 70 |
-
"
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
"
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
#
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
for
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
"
|
| 94 |
-
"
|
| 95 |
-
"
|
| 96 |
-
"
|
| 97 |
-
"
|
| 98 |
-
"
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
#
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
""
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
orchestrator_worker_builder.add_node("
|
| 214 |
-
orchestrator_worker_builder.add_node("
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
orchestrator_worker_builder.
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
)
|
| 222 |
-
orchestrator_worker_builder.add_edge("
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
#
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
'
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
""
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
"
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
""
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
optimizer_builder.add_node("
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
optimizer_builder.add_edge(
|
| 335 |
-
optimizer_builder.
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
route_roadmap
|
| 339 |
-
|
| 340 |
-
"
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
#
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
shifted_tasks_roadmap =
|
| 354 |
-
shifted_tasks_roadmap
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
"
|
| 378 |
-
"
|
| 379 |
-
"
|
| 380 |
-
"
|
| 381 |
-
"
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
st.
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
st.
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
st.
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
st.sidebar.
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 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()
|