The foundation of this project is the M5 Agentic Workflow where the LLM generates executable Python code to perform booking operations. This code-as-action pattern enables dynamic business logic without hardcoding rules:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import google.generativeai as genai
from tinydb import TinyDB, Query
from datetime import datetime, timedelta
import re
app = FastAPI(title="Badminton Court Booking Agent")
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
model = genai.GenerativeModel('gemini-2.5-flash')
db = TinyDB('bookings.json')
bookings_table = db.table('bookings')
class BookingRequest(BaseModel):
user_message: str
ic_number: str = None
class AgenticBookingAgent:
"""
M5 Agentic Workflow implementation for badminton court bookings.
The LLM generates Python code to execute booking operations.
"""
def __init__(self):
self.model = model
self.db = bookings_table
self.system_context = """
You are an AI agent that generates Python code to manage badminton court bookings.
AVAILABLE FUNCTIONS:
- get_bookings() -> List[Dict]: Returns all bookings from database
- create_booking(court, date, start_time, end_time, ic, name) -> Dict
- cancel_booking(ic, court, date) -> Dict
- check_availability(court, date, start_time, end_time) -> bool
- calculate_price(start_time, end_time, date) -> float
PRICING RULES:
- Daytime (6am-6pm): Weekday RM80/hr, Weekend RM90/hr
- Nighttime (6pm-6am): Weekday RM100/hr, Weekend RM120/hr
IC NUMBER FORMAT: YYMMDD-PB-###G (e.g., 850715-08-1234)
COURTS: 1-12 available
Generate executable Python code to handle the user's request.
Return only the code, no explanations.
"""
async def process_request(self, user_message: str, ic_number: str = None) -> Dict:
"""
Main agentic workflow: Analyze request -> Generate code -> Execute -> Return result
"""
generated_code = await self._generate_action_code(user_message, ic_number)
if not self._validate_code_safety(generated_code):
raise HTTPException(status_code=400, detail="Generated code failed security validation")
result = await self._execute_code(generated_code)
return {
"success": True,
"result": result,
"generated_code": generated_code if os.getenv("DEBUG") else None
}
async def _generate_action_code(self, user_message: str, ic_number: str) -> str:
"""
Use Gemini 2.5 Flash to generate executable Python code based on user request.
This is the core of the Code-as-Action pattern.
"""
prompt = f"""
{self.system_context}
USER REQUEST: {user_message}
IC NUMBER: {ic_number if ic_number else "Not provided"}
CURRENT DATE: {datetime.now().strftime('%Y-%m-%d')}
CURRENT TIME: {datetime.now().strftime('%H:%M')}
Generate Python code to handle this request. The code should:
1. Parse the user's intent (book, cancel, check availability, query)
2. Validate inputs (IC format, court number, date/time)
3. Check for conflicts if booking
4. Calculate pricing if booking
5. Execute the database operation
6. Return a clear result message
Code must use only the provided functions and follow all pricing rules.
"""
response = self.model.generate_content(prompt)
code = self._extract_code_from_response(response.text)
return code
def _extract_code_from_response(self, response_text: str) -> str:
"""Extract Python code from LLM response, handling markdown code blocks."""
code = re.sub(r'```python\n', '', response_text)
code = re.sub(r'```\n?', '', code)
code = code.strip()
return code
def _validate_code_safety(self, code: str) -> bool:
"""
Security validation to prevent malicious code execution.
Checks for dangerous operations and unauthorized imports.
"""
dangerous_patterns = [
r'import\s+os',
r'import\s+sys',
r'import\s+subprocess',
r'__import__',
r'eval\(',
r'exec\(',
r'open\(',
r'file\(',
r'compile\('
]
for pattern in dangerous_patterns:
if re.search(pattern, code):
return False
allowed_imports = ['datetime', 'timedelta', 're']
return True
async def _execute_code(self, code: str) -> Dict:
"""
Execute generated code in a controlled namespace with access to booking functions.
This is where the AI-generated code actually runs.
"""
namespace = {
'get_bookings': self._get_bookings,
'create_booking': self._create_booking,
'cancel_booking': self._cancel_booking,
'check_availability': self._check_availability,
'calculate_price': self._calculate_price,
'datetime': datetime,
'timedelta': timedelta,
're': re,
'result': None
}
try:
exec(code, namespace)
return namespace.get('result', {"message": "Operation completed"})
except Exception as e:
return {
"error": True,
"message": f"Code execution failed: {str(e)}"
}