""" Phase 1 #1A: Skills API Router POST /api/skills/ppt/generate - Generate PPTX GET /api/skills/ppt/download/{instance_id} - Download PPTX POST /api/skills/seo/analyze - SEO analysis POST /api/skills/research/generate - Investment research GET /api/skills/status/{instance_id} - Check execution status """ from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import FileResponse from pydantic import BaseModel from app.auth import get_current_user, get_current_tenant from app.skill_runtime import PPTSkillRuntime, SEOSkillRuntime, ResearchSkillRuntime from app.database import SessionLocal import os router = APIRouter(tags=["技能执行"]) class PPTRequest(BaseModel): content: str template: str = "business" slides: int = 5 class SEORequest(BaseModel): url: str = "" keywords: str = "" class ResearchRequest(BaseModel): symbol: str @router.post("/ppt/generate") async def generate_ppt(req: PPTRequest, user=Depends(get_current_user), tenant=Depends(get_current_tenant)): """Generate PPT from text content using AI + python-pptx.""" runtime = PPTSkillRuntime( skill_id="ppt-generator", user_id=user["id"], tenant_id=tenant ) result = await runtime.execute({"content": req.content, "template": req.template, "slides": req.slides}) return {"code": 0, "data": result, "message": "PPT generated" if result["status"] == "ready" else "Failed"} @router.get("/ppt/download/{instance_id}") async def download_ppt(instance_id: str, user=Depends(get_current_user)): """Download generated PPTX file.""" from sqlalchemy import text db = SessionLocal() row = db.execute(text( "SELECT result FROM skill_instances WHERE id = :id AND user_id = :uid" ), {"id": instance_id, "uid": user["id"]}).fetchone() db.close() if not row: raise HTTPException(404, "Instance not found") # Find workspace path ws = f"/app/workspace/*/{user['id']}/ppt-generator/{instance_id}/output.pptx" import glob matches = glob.glob(ws) if matches: return FileResponse(matches[0], media_type="application/vnd.openxmlformats-officedocument.presentationml.presentation", filename="presentation.pptx") raise HTTPException(404, "PPTX file not found") @router.post("/seo/analyze") async def analyze_seo(req: SEORequest, user=Depends(get_current_user), tenant=Depends(get_current_tenant)): """Analyze SEO for a URL/keyword.""" runtime = SEOSkillRuntime( skill_id="seo-optimizer", user_id=user["id"], tenant_id=tenant ) result = await runtime.execute({"url": req.url, "keywords": req.keywords}) return {"code": 0, "data": result, "message": "SEO analysis complete"} @router.post("/research/generate") async def generate_research(req: ResearchRequest, user=Depends(get_current_user), tenant=Depends(get_current_tenant)): """Generate investment research report.""" runtime = ResearchSkillRuntime( skill_id="investment-research", user_id=user["id"], tenant_id=tenant ) result = await runtime.execute({"symbol": req.symbol}) return {"code": 0, "data": result, "message": "Research generated"} @router.get("/status/{instance_id}") async def get_skill_status(instance_id: str, user=Depends(get_current_user)): """Check skill execution status.""" from sqlalchemy import text db = SessionLocal() row = db.execute(text( "SELECT status, result, error, created_at FROM skill_instances WHERE id = :id AND user_id = :uid" ), {"id": instance_id, "uid": user["id"]}).fetchone() db.close() if not row: raise HTTPException(404, "Instance not found") return {"code": 0, "data": {"status": row[0], "result": row[1], "error": row[2], "created_at": str(row[3])}}