KamalamSivakumar commited on
Commit
4f0685d
ยท
verified ยท
1 Parent(s): c8ffb23

Upload 4 files

Browse files
Files changed (4) hide show
  1. .env +1 -0
  2. __init__.py +1 -0
  3. agent.py +196 -0
  4. streamlit_app.py.py +96 -0
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ GOOGLE_API_KEY="AIzaSyA7clyHGJaeyXta_b32VLxGAKhP0ZxOlfc"
__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from . import agent
agent.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from google.adk.agents import Agent
2
+ from google.adk.tools import BaseTool, ToolContext
3
+ from google.adk.models import LlmRequest, LlmResponse
4
+ from google.adk.tools import FunctionTool
5
+ from google.adk.agents import Agent
6
+ import requests
7
+ from datetime import datetime, timedelta
8
+ from typing import List, Optional
9
+ import json
10
+ from datetime import datetime, timedelta
11
+ from typing import Optional, List, Dict
12
+ from dateutil import parser
13
+ import requests
14
+
15
+ class ExtractScheduleDetailsTool(BaseTool):
16
+ def __init__(self):
17
+ super().__init__(
18
+ name="extract_schedule_details",
19
+ description="Extracts date, time, and attendee emails from a task description."
20
+ )
21
+
22
+ async def run_llm(self, tool_context: ToolContext, task: str) -> Dict:
23
+ prompt = f"""
24
+ You will be given a user task. Extract the following if present:
25
+ - date (in YYYY-MM-DD)
26
+ - time (in HH:MM 24-hr format)
27
+ - location
28
+ - attendees (only email addresses)
29
+
30
+ Respond in JSON like this:
31
+ {{
32
+ "date": "...",
33
+ "time": "...",
34
+ "location": "...",
35
+ "attendees": ["[email protected]", "[email protected]"]
36
+ }}
37
+
38
+ If any field is missing, set it to null or empty list.
39
+ Task: {task}
40
+ """
41
+ llm_request = LlmRequest(prompt=prompt.strip())
42
+ llm_response: LlmResponse = await tool_context.llm.complete(llm_request)
43
+ try:
44
+ return json.loads(llm_response.text)
45
+ except Exception:
46
+ return {"date": None, "time": None, "location": None, "attendees": []}
47
+
48
+ # -- TOOL 1: Decompose Task --
49
+ class DecomposeTaskTool(BaseTool):
50
+ def __init__(self):
51
+ super().__init__(
52
+ name="decompose_task",
53
+ description="Decomposes a task into subtasks and estimates XP using prompting."
54
+ )
55
+
56
+ async def run_llm(self, tool_context: ToolContext, task: str) -> str:
57
+ prompt = f"""
58
+ You are an intelligent task planner that receives a user task and decides whether the task needs to be broken down into subtasks.
59
+
60
+ Respond in the following format:
61
+
62
+ ---
63
+ task: {task}
64
+
65
+ If the task is simple and doesnโ€™t need subtasks:
66
+ subtasks required: 0
67
+ note: This task is straightforward and does not require subtasking.
68
+
69
+ If the task needs to be broken down:
70
+ subtasks required: <number of subtasks>
71
+ subtask1: <subtask description> | XP: <estimated XP>
72
+ subtask2: <subtask description> | XP: <estimated XP>
73
+ ...
74
+ total XP: <sum of all XP values>
75
+ ---
76
+
77
+ Guidelines:
78
+ - Skip subtasks for trivial tasks like โ€œwater the plantsโ€.
79
+ - XP should reflect effort (sum up to 100 if fully scoped).
80
+ """
81
+ llm_request = LlmRequest(prompt=prompt.strip())
82
+ llm_response: LlmResponse = await tool_context.llm.complete(llm_request)
83
+ return llm_response.text.strip()
84
+
85
+ async def run(self, tool_context: ToolContext, task: str) -> str:
86
+ return await self.run_llm(tool_context, task)
87
+
88
+ # -- TOOL 2: Estimate XP --
89
+ class EstimateXPTool(BaseTool):
90
+ def __init__(self):
91
+ super().__init__(
92
+ name="estimate_xp",
93
+ description="Estimates XP score for subtasks."
94
+ )
95
+
96
+ async def run(self, tool_context: ToolContext, task: str, subtasks: List[str]) -> Dict:
97
+ xp_per_subtask = {}
98
+ for i, subtask in enumerate(subtasks):
99
+ xp_per_subtask[subtask] = 10 + 5 * i
100
+ total_xp = sum(xp_per_subtask.values())
101
+
102
+ return {
103
+ "status": "success",
104
+ "report": {
105
+ "task": task,
106
+ "subtasks_required": len(subtasks),
107
+ "subtask_details": [{"subtask": s, "xp": xp_per_subtask[s]} for s in subtasks],
108
+ "total_xp": total_xp
109
+ }
110
+ }
111
+
112
+ def schedule_event(
113
+ date: str,
114
+ time: Optional[str] = None,
115
+ location: str = "",
116
+ description: str = "",
117
+ attendees: Optional[List[Dict[str,str]]] = None
118
+ ) -> str:
119
+ event_details = {
120
+ "summary": description,
121
+ "location": location,
122
+ "description": description,
123
+ "timeZone": "Asia/Kolkata"
124
+ }
125
+
126
+ try:
127
+ if time and time.lower() != "unknown":
128
+ # Try to parse the time using dateutil for flexibility
129
+ parsed_time = parser.parse(time)
130
+ start_datetime = datetime.strptime(date, "%Y-%m-%d").replace(
131
+ hour=parsed_time.hour, minute=parsed_time.minute, second=0
132
+ )
133
+ end_datetime = start_datetime + timedelta(minutes=30)
134
+
135
+ # Format to ISO strings for scheduling
136
+ event_details["start"] = start_datetime.isoformat()
137
+ event_details["end"] = end_datetime.isoformat()
138
+ else:
139
+ # # All-day event
140
+ event_details["start"] = date
141
+ event_details["end"] = date
142
+ # event_details["allDay"] = True
143
+
144
+ except Exception as e:
145
+ return f"Error parsing time: {str(e)}"
146
+
147
+ if attendees:
148
+ event_details["attendees"] = [email for email in attendees]
149
+
150
+ try:
151
+ print(event_details) #http://127.0.0.1:5000/schedule
152
+ response = requests.post("https://e04c-49-206-114-222.ngrok-free.app/schedule", json=event_details) #https://d49c-49-206-114-222.ngrok-free.app
153
+ if response.status_code == 200:
154
+ return f"Event scheduled: {description} on {date}"
155
+ else:
156
+ return f"Failed to schedule event. Server response: {response.text}"
157
+ except Exception as e:
158
+ return f"Error during scheduling: {str(e)}"
159
+
160
+ schedule_event_tool = FunctionTool(func=schedule_event)
161
+
162
+ # -- ROOT AGENT --
163
+ root_agent = Agent(
164
+ name="personaliser",
165
+ description="Agent to gamify tasks and create calendar events.",
166
+ model="gemini-2.0-flash",
167
+ instruction=("""
168
+ You are a productivity assistant that gamifies and schedules tasks.
169
+
170
+ Your workflow:
171
+ 1. Detect the task.
172
+ 2. Gamify it using `decompose_task` and `estimate_xp`.
173
+ 3. Use `extract_schedule_details` to identify if a date/time is mentioned.
174
+ 4. If the task has:
175
+ - a date
176
+ - a valid description (the task itself)
177
+ Then call `schedule_event`.
178
+
179
+ Always summarize in this format:
180
+ - Main Task
181
+ - Subtasks (with XP)
182
+ - Total XP
183
+ - Event Details (date, time, location, attendees)
184
+
185
+ **Important**:
186
+ - Never show internal tool names, JSON structures, or debug logs to the user.
187
+ - Your tone should be friendly, helpful, and focused on making the user's tasks more enjoyable and efficient.
188
+ - If the task is trivial (e.g., โ€œwater the plantsโ€), skip subtasks but still assign an XP score and acknowledge completion."""
189
+ ),
190
+ tools=[
191
+ DecomposeTaskTool(),
192
+ EstimateXPTool(),
193
+ ExtractScheduleDetailsTool(),
194
+ schedule_event_tool
195
+ ]
196
+ )
streamlit_app.py.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ import streamlit as st
4
+ from dotenv import load_dotenv
5
+
6
+ from agent import root_agent
7
+ from google.adk.sessions import InMemorySessionService
8
+ from google.adk.runners import Runner
9
+ from google.genai.types import Content, Part
10
+
11
+ # Load environment variables
12
+ load_dotenv()
13
+ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
14
+
15
+ if not GOOGLE_API_KEY:
16
+ st.error("โŒ GOOGLE_API_KEY is missing! Please set it in a .env file.")
17
+ st.stop()
18
+
19
+ # Streamlit page setup
20
+ st.set_page_config(page_title="๐ŸŽฎ Task Gamifier", layout="wide")
21
+ st.title("๐Ÿ“… Gamify & Schedule Tasks")
22
+
23
+ # -------------------------------
24
+ # ๐Ÿšช Exit Button & Session Reset
25
+ # -------------------------------
26
+ if st.button("๐Ÿ›‘ Exit & Restart Session"):
27
+ for key in ["session", "runner", "history"]:
28
+ st.session_state.pop(key, None)
29
+ st.success("โœ… Session restarted.")
30
+ st.rerun()
31
+
32
+ # -------------------------------
33
+ # ๐Ÿง  Initialize session + runner
34
+ # -------------------------------
35
+ if "session_service" not in st.session_state:
36
+ st.session_state.session_service = InMemorySessionService()
37
+
38
+ if "session" not in st.session_state:
39
+ st.session_state.session = asyncio.run(
40
+ st.session_state.session_service.create_session(
41
+ app_name="task_gamifier_app",
42
+ user_id="user-001"
43
+ )
44
+ )
45
+
46
+ if "runner" not in st.session_state:
47
+ st.session_state.runner = Runner(
48
+ app_name=st.session_state.session.app_name,
49
+ agent=root_agent,
50
+ session_service=st.session_state.session_service
51
+ )
52
+
53
+ if "history" not in st.session_state:
54
+ st.session_state.history = []
55
+
56
+ # -------------------------------
57
+ # โœ๏ธ Task Input Form
58
+ # -------------------------------
59
+ with st.form(key="task_form", clear_on_submit=True):
60
+ task_input = st.text_input("๐Ÿ“ What task do you want to gamify & schedule?", key="task_input")
61
+ submitted = st.form_submit_button("๐Ÿš€ Submit")
62
+
63
+ if submitted and task_input:
64
+ async def process():
65
+ final_response = None
66
+ async for event in st.session_state.runner.run_async(
67
+ session_id=st.session_state.session.id,
68
+ user_id=st.session_state.session.user_id,
69
+ new_message=Content(role="user", parts=[Part(text=task_input)])
70
+ ):
71
+ if event.is_final_response():
72
+ final_response = event.content.parts[0].text
73
+ return final_response
74
+
75
+ try:
76
+ response = asyncio.run(process())
77
+ st.session_state.history.append((task_input, response))
78
+ st.rerun()
79
+ except Exception as e:
80
+ st.error(f"โš ๏ธ Error: {e}")
81
+
82
+ # -------------------------------
83
+ # ๐Ÿ’ฌ Chat History
84
+ # -------------------------------
85
+ st.subheader("๐Ÿ’ฌ Interaction History")
86
+ for user_input, agent_response in reversed(st.session_state.history):
87
+ st.markdown(f"**You:** {user_input}")
88
+ st.markdown(f"๐Ÿค– **Agent:** {agent_response}")
89
+ st.markdown("---")
90
+
91
+ # -------------------------------
92
+ # ๐Ÿงน Clear Chat History
93
+ # -------------------------------
94
+ if st.button("Clear Chat Only"):
95
+ st.session_state.history = []
96
+ st.rerun()