"""ObsiGate AI — API routes for AI-powered editor features.""" from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel, Field from typing import Optional import logging from backend.ai import ( ai_improve_writing, ai_fix_spelling, ai_make_shorter, ai_make_longer, ai_simplify, ai_change_tone, ai_translate, ai_explain, ai_summarize, ai_continue_writing, ai_custom_rewrite, ai_convert_to_list, ai_convert_to_table, ai_generate_frontmatter, ai_inline_complete, ai_convert_to_canvas, ) logger = logging.getLogger("obsigate.ai_routes") router = APIRouter(prefix="/api/ai", tags=["AI"]) class AIRequest(BaseModel): text: str = Field(..., description="Input text to process", min_length=1) instruction: Optional[str] = Field(None, description="Custom instruction for rewrite") target_lang: Optional[str] = Field(None, description="Target language for translation") tone: Optional[str] = Field(None, description="Target tone (professional, casual, etc.)") provider: Optional[str] = Field(None, description="AI provider override") class AIResponse(BaseModel): result: str = Field(..., description="Processed text result") provider: str = Field(..., description="AI provider used") async def _handle(action, request: AIRequest): """Wrapper with error handling.""" try: result = await action(request.text, request.provider) return AIResponse(result=result, provider=request.provider or "default") except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"AI error: {e}") raise HTTPException(status_code=500, detail=f"AI service error: {str(e)}") @router.post("/improve", response_model=AIResponse) async def api_improve(req: AIRequest): """Improve writing quality.""" return await _handle(ai_improve_writing, req) @router.post("/fix-spelling", response_model=AIResponse) async def api_fix_spelling(req: AIRequest): """Fix spelling and grammar.""" return await _handle(ai_fix_spelling, req) @router.post("/make-shorter", response_model=AIResponse) async def api_make_shorter(req: AIRequest): """Make text more concise.""" return await _handle(ai_make_shorter, req) @router.post("/make-longer", response_model=AIResponse) async def api_make_longer(req: AIRequest): """Expand text with more detail.""" return await _handle(ai_make_longer, req) @router.post("/simplify", response_model=AIResponse) async def api_simplify(req: AIRequest): """Simplify language.""" return await _handle(ai_simplify, req) @router.post("/tone", response_model=AIResponse) async def api_tone(req: AIRequest): """Change text tone. Requires `tone` field (e.g., 'professional', 'casual').""" if not req.tone: raise HTTPException(status_code=400, detail="Field 'tone' is required (e.g., 'professional', 'casual')") return await _handle(lambda text, p: ai_change_tone(text, req.tone, p), req) @router.post("/translate", response_model=AIResponse) async def api_translate(req: AIRequest): """Translate text. Requires `target_lang` field (e.g., 'French', 'English', 'Japanese').""" if not req.target_lang: raise HTTPException(status_code=400, detail="Field 'target_lang' is required") return await _handle(lambda text, p: ai_translate(text, req.target_lang, p), req) @router.post("/explain", response_model=AIResponse) async def api_explain(req: AIRequest): """Explain the selected text.""" return await _handle(ai_explain, req) @router.post("/summarize", response_model=AIResponse) async def api_summarize(req: AIRequest): """Summarize text.""" return await _handle(ai_summarize, req) @router.post("/continue", response_model=AIResponse) async def api_continue(req: AIRequest): """Continue writing from the selected text.""" return await _handle(ai_continue_writing, req) @router.post("/rewrite", response_model=AIResponse) async def api_rewrite(req: AIRequest): """Custom rewrite with instruction. Requires `instruction` field.""" if not req.instruction: raise HTTPException(status_code=400, detail="Field 'instruction' is required") return await _handle(lambda text, p: ai_custom_rewrite(text, req.instruction, p), req) @router.post("/to-list", response_model=AIResponse) async def api_to_list(req: AIRequest): """Convert text to a markdown list.""" return await _handle(ai_convert_to_list, req) @router.post("/to-table", response_model=AIResponse) async def api_to_table(req: AIRequest): """Convert text to a markdown table.""" return await _handle(ai_convert_to_table, req) @router.post("/frontmatter", response_model=AIResponse) async def api_frontmatter(req: AIRequest): """Generate YAML frontmatter.""" return await _handle(ai_generate_frontmatter, req) @router.post("/inline-complete", response_model=AIResponse) async def api_inline_complete(req: AIRequest): """Inline completion.""" return await _handle(ai_inline_complete, req) @router.post("/to-canvas", response_model=AIResponse) async def api_to_canvas(req: AIRequest): """Convert to Mermaid diagram or outline.""" return await _handle(ai_convert_to_canvas, req)