from fastapi import APIRouter, Depends, HTTPException from app.middleware.auth import get_current_user_id from app.models import ( ProactiveExecuteRequest, ProactiveExecuteResponse, ProactivePreference, ProactivePreferencesResponse, ProactiveRespondRequest, ProactiveRespondResponse, ) from app.services.db import get_pool router = APIRouter(prefix="/proactive", tags=["proactive"]) @router.post("/respond", response_model=ProactiveRespondResponse) async def respond_to_action(req: ProactiveRespondRequest, user_id: str = Depends(get_current_user_id)): pool = await get_pool() row = await pool.fetchrow( "SELECT id, user_id FROM proactive_actions WHERE id = $1 AND user_id = $2::uuid", req.proactive_action_id, user_id, ) if not row: raise HTTPException(status_code=404, detail="Proactive action not found") await pool.execute( "UPDATE proactive_actions SET user_choice = $1, chosen_action = $2, responded_at = now() WHERE id = $3", req.user_choice, req.chosen_action, req.proactive_action_id, ) return ProactiveRespondResponse( logged=True, should_execute=req.user_choice == "accepted", ) @router.post("/execute", response_model=ProactiveExecuteResponse) async def execute_action(req: ProactiveExecuteRequest, user_id: str = Depends(get_current_user_id)): pool = await get_pool() row = await pool.fetchrow( "SELECT id, user_choice FROM proactive_actions WHERE id = $1 AND user_id = $2::uuid", req.proactive_action_id, user_id, ) if not row: raise HTTPException(status_code=404, detail="Proactive action not found") # Mark as executed — actual execution happens device-side (AppleScript/Computer Use) # This endpoint logs that it was executed and can store results await pool.execute( "UPDATE proactive_actions SET executed = true WHERE id = $1", req.proactive_action_id, ) return ProactiveExecuteResponse( executed=True, result=f"Action {req.action_type} marked as executed. Device-side execution handles the actual work.", ) @router.get("/preferences", response_model=ProactivePreferencesResponse) async def get_preferences(user_id: str = Depends(get_current_user_id)): pool = await get_pool() rows = await pool.fetch( """SELECT friction_type, COUNT(*) as total, COUNT(*) FILTER (WHERE user_choice = 'accepted') as accepted, (SELECT chosen_action FROM proactive_actions pa2 WHERE pa2.user_id = $1::uuid AND pa2.friction_type = pa.friction_type AND pa2.user_choice = 'accepted' GROUP BY chosen_action ORDER BY COUNT(*) DESC LIMIT 1) as top_action FROM proactive_actions pa WHERE user_id = $1::uuid AND user_choice IS NOT NULL GROUP BY friction_type""", user_id, ) preferences = {} for r in rows: total = r["total"] accepted = r["accepted"] preferences[r["friction_type"]] = ProactivePreference( preferred_action=r["top_action"], total_choices=total, acceptance_rate=accepted / total if total > 0 else 0.0, ) return ProactivePreferencesResponse(preferences=preferences)