""" api/routes/ai.py - AI-powered generation endpoints """ import asyncio import flask import jwt import os import ai.parser as ai_parser JWT_SECRET = os.getenv("JWT_SECRET") def _get_user_uuid(request): """Extract and validate user UUID from JWT token.""" auth_header = request.headers.get("Authorization", "") if not auth_header.startswith("Bearer "): return None token = auth_header[7:] try: payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) return payload.get("sub") except (jwt.ExpiredSignatureError, jwt.InvalidTokenError): return None def register(app): @app.route("/api/ai/generate-steps", methods=["POST"]) def api_generate_steps(): """ Generate ADHD-friendly routine steps from a goal description. Body: {"goal": string} Returns: {"steps": [{"name": string, "duration_minutes": int}]} """ user_uuid = _get_user_uuid(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() if not data: return flask.jsonify({"error": "missing body"}), 400 goal = data.get("goal", "").strip() if not goal: return flask.jsonify({"error": "missing required field: goal"}), 400 if len(goal) > 500: return flask.jsonify({"error": "goal too long (max 500 characters)"}), 400 try: result = asyncio.run(ai_parser.parse(goal, "step_generator")) except Exception as e: return flask.jsonify({"error": f"AI service error: {str(e)}"}), 500 if "error" in result: return flask.jsonify({"error": result["error"]}), 500 steps = result.get("steps", []) validated = [] for s in steps: if not isinstance(s, dict): continue name = str(s.get("name", "")).strip() if not name: continue try: dur = max(1, min(60, int(s.get("duration_minutes", 5)))) except (ValueError, TypeError): dur = 5 validated.append({"name": name, "duration_minutes": dur}) if len(validated) < 2: return flask.jsonify({"error": "AI failed to generate valid steps"}), 500 return flask.jsonify({"steps": validated}), 200