""" Fyltr Form Generator Service (LangGraph). POST /generate-form: full form from description. POST /refine-form: surgical diff (changes, additions, removals). """ import logging import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from dotenv import load_dotenv from schema import ( GenerateFormRequest, GenerateFormResponse, RefineFormRequest, RefineFormResponse, RefineDiff, PlanFormRequest, PlanFormResponse, PlanFormQuestion, PlanFormQuestionOption, ) from src.form_generator_workflow import workflow_instance, plan_form_request load_dotenv() logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="Fyltr Form Generator Service", version="1.0.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/") async def root(): return {"message": "Fyltr Form Generator Service", "status": "running"} @app.get("/health") async def health(): return {"status": "healthy", "service": "form-generator"} @app.post("/plan-form-request", response_model=PlanFormResponse) async def plan_form_request_route(request: PlanFormRequest): """Lightweight preflight: decide if we need clarifying questions. No balance deduction.""" if not request.description or not request.description.strip(): return PlanFormResponse( should_ask_questions=False, questions=[], reasoning_summary="", ) if not os.getenv("OPENAI_API_KEY"): return PlanFormResponse( should_ask_questions=False, questions=[], reasoning_summary="Service not configured.", ) result = plan_form_request( description=request.description.strip(), current_fields=request.current_fields, current_title=request.current_title or "", ) if not result.get("success"): return PlanFormResponse( should_ask_questions=False, questions=[], reasoning_summary=result.get("error") or "Could not analyze request.", ) questions = [] for q in result.get("questions") or []: opts = None if q.get("options"): opts = [PlanFormQuestionOption(value=o.get("value", ""), label=o.get("label", "")) for o in q["options"] if isinstance(o, dict)] questions.append( PlanFormQuestion( id=q.get("id", "q"), label=q.get("label", ""), type=q.get("type", "text"), options=opts, required=bool(q.get("required", True)), ) ) return PlanFormResponse( should_ask_questions=result.get("should_ask_questions", False), questions=questions, reasoning_summary=result.get("reasoning_summary", "") or "Ready.", ) @app.post("/generate-form", response_model=GenerateFormResponse) async def generate_form(request: GenerateFormRequest): if not request.description or not request.description.strip(): raise HTTPException(status_code=400, detail="description is required and must be non-empty") if not os.getenv("OPENAI_API_KEY"): raise HTTPException(status_code=500, detail="OPENAI_API_KEY not configured") result = workflow_instance.generate_form( request.description.strip(), conversation_context=request.conversation_context, user_goals=request.user_goals, preferred_field_types=request.preferred_field_types, must_have_logic=request.must_have_logic, ) if not result.get("success") or result.get("formJSON") is None: detail = result.get("error") or "Form generation failed" raise HTTPException(status_code=422, detail=detail) return GenerateFormResponse( formJSON=result["formJSON"], metadata=result.get("metadata") or {}, warnings=result.get("warnings"), ) @app.post("/refine-form", response_model=RefineFormResponse) async def refine_form(request: RefineFormRequest): if not request.user_request or not request.user_request.strip(): raise HTTPException(status_code=400, detail="user_request is required and must be non-empty") if not os.getenv("OPENAI_API_KEY"): raise HTTPException(status_code=500, detail="OPENAI_API_KEY not configured") result = workflow_instance.refine_form( user_request=request.user_request.strip(), current_fields=request.current_fields, current_title=request.current_title or "", conversation_context=request.conversation_context, ) if not result.get("success") or result.get("diff") is None: detail = result.get("error") or "Form refine failed" raise HTTPException(status_code=422, detail=detail) diff = result["diff"] return RefineFormResponse( diff=RefineDiff( changes=diff.get("changes", []), additions=diff.get("additions", []), removals=diff.get("removals", []), ) )