Files
LockInBroAPI/app/routers/proactive.py
2026-03-29 06:57:34 -04:00

97 lines
3.3 KiB
Python

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)