Spaces:
Runtime error
Runtime error
from google.adk.agents import Agent | |
from google.adk.tools import BaseTool, ToolContext | |
from google.adk.models import LlmRequest, LlmResponse | |
from google.adk.tools import FunctionTool | |
from google.adk.agents import Agent | |
import requests | |
from datetime import datetime, timedelta | |
from typing import List, Optional | |
import json | |
from datetime import datetime, timedelta | |
from typing import Optional, List, Dict | |
from dateutil import parser | |
import requests | |
class ExtractScheduleDetailsTool(BaseTool): | |
def __init__(self): | |
super().__init__( | |
name="extract_schedule_details", | |
description="Extracts date, time, and attendee emails from a task description." | |
) | |
async def run_llm(self, tool_context: ToolContext, task: str) -> Dict: | |
prompt = f""" | |
You will be given a user task. Extract the following if present: | |
- date (in YYYY-MM-DD) | |
- time (in HH:MM 24-hr format) | |
- location | |
- attendees (only email addresses) | |
Respond in JSON like this: | |
{{ | |
"date": "...", | |
"time": "...", | |
"location": "...", | |
"attendees": ["email1@example.com", "email2@example.com"] | |
}} | |
If any field is missing, set it to null or empty list. | |
Task: {task} | |
""" | |
llm_request = LlmRequest(prompt=prompt.strip()) | |
llm_response: LlmResponse = await tool_context.llm.complete(llm_request) | |
try: | |
return json.loads(llm_response.text) | |
except Exception: | |
return {"date": None, "time": None, "location": None, "attendees": []} | |
# -- TOOL 1: Decompose Task -- | |
class DecomposeTaskTool(BaseTool): | |
def __init__(self): | |
super().__init__( | |
name="decompose_task", | |
description="Decomposes a task into subtasks and estimates XP using prompting." | |
) | |
async def run_llm(self, tool_context: ToolContext, task: str) -> str: | |
prompt = f""" | |
You are an intelligent task planner that receives a user task and decides whether the task needs to be broken down into subtasks. | |
Respond in the following format: | |
--- | |
task: {task} | |
If the task is simple and doesn’t need subtasks: | |
subtasks required: 0 | |
note: This task is straightforward and does not require subtasking. | |
If the task needs to be broken down: | |
subtasks required: <number of subtasks> | |
subtask1: <subtask description> | XP: <estimated XP> | |
subtask2: <subtask description> | XP: <estimated XP> | |
... | |
total XP: <sum of all XP values> | |
--- | |
Guidelines: | |
- Skip subtasks for trivial tasks like “water the plants”. | |
- XP should reflect effort (sum up to 100 if fully scoped). | |
""" | |
llm_request = LlmRequest(prompt=prompt.strip()) | |
llm_response: LlmResponse = await tool_context.llm.complete(llm_request) | |
return llm_response.text.strip() | |
async def run(self, tool_context: ToolContext, task: str) -> str: | |
return await self.run_llm(tool_context, task) | |
# -- TOOL 2: Estimate XP -- | |
class EstimateXPTool(BaseTool): | |
def __init__(self): | |
super().__init__( | |
name="estimate_xp", | |
description="Estimates XP score for subtasks." | |
) | |
async def run(self, tool_context: ToolContext, task: str, subtasks: List[str]) -> Dict: | |
xp_per_subtask = {} | |
for i, subtask in enumerate(subtasks): | |
xp_per_subtask[subtask] = 10 + 5 * i | |
total_xp = sum(xp_per_subtask.values()) | |
return { | |
"status": "success", | |
"report": { | |
"task": task, | |
"subtasks_required": len(subtasks), | |
"subtask_details": [{"subtask": s, "xp": xp_per_subtask[s]} for s in subtasks], | |
"total_xp": total_xp | |
} | |
} | |
def schedule_event( | |
date: str, | |
time: Optional[str] = None, | |
location: str = "", | |
description: str = "", | |
attendees: Optional[List[Dict[str,str]]] = None | |
) -> str: | |
event_details = { | |
"summary": description, | |
"location": location, | |
"description": description, | |
"timeZone": "Asia/Kolkata" | |
} | |
try: | |
if time and time.lower() != "unknown": | |
# Try to parse the time using dateutil for flexibility | |
parsed_time = parser.parse(time) | |
start_datetime = datetime.strptime(date, "%Y-%m-%d").replace( | |
hour=parsed_time.hour, minute=parsed_time.minute, second=0 | |
) | |
end_datetime = start_datetime + timedelta(minutes=30) | |
# Format to ISO strings for scheduling | |
event_details["start"] = start_datetime.isoformat() | |
event_details["end"] = end_datetime.isoformat() | |
else: | |
# # All-day event | |
event_details["start"] = date | |
event_details["end"] = date | |
# event_details["allDay"] = True | |
except Exception as e: | |
return f"Error parsing time: {str(e)}" | |
if attendees: | |
event_details["attendees"] = [email for email in attendees] | |
try: | |
print(event_details) #http://127.0.0.1:5000/schedule | |
response = requests.post("http://localhost:7861/schedule", json=event_details) #https://d49c-49-206-114-222.ngrok-free.app | |
if response.status_code == 200: | |
return f"Event scheduled: {description} on {date}" | |
else: | |
return f"Failed to schedule event. Server response: {response.text}" | |
except Exception as e: | |
return f"Error during scheduling: {str(e)}" | |
schedule_event_tool = FunctionTool(func=schedule_event) | |
# -- ROOT AGENT -- | |
root_agent = Agent( | |
name="personaliser", | |
description="Agent to gamify tasks and create calendar events.", | |
model="gemini-2.0-flash", | |
instruction=(""" | |
You are a productivity assistant that gamifies and schedules tasks. | |
Your workflow: | |
1. Detect the task. | |
2. Gamify it using `decompose_task` and `estimate_xp`. | |
3. Use `extract_schedule_details` to identify if a date/time is mentioned. | |
4. If the task has: | |
- a date | |
- a valid description (the task itself) | |
Then call `schedule_event`. | |
Always summarize in this format: | |
- Main Task | |
- Subtasks (with XP) | |
- Total XP | |
- Event Details (date, time, location, attendees) | |
**Important**: | |
- Never show internal tool names, JSON structures, or debug logs to the user. | |
- Your tone should be friendly, helpful, and focused on making the user's tasks more enjoyable and efficient. | |
- If the task is trivial (e.g., “water the plants”), skip subtasks but still assign an XP score and acknowledge completion.""" | |
), | |
tools=[ | |
DecomposeTaskTool(), | |
EstimateXPTool(), | |
ExtractScheduleDetailsTool(), | |
schedule_event_tool | |
] | |
) | |