DevPilot / src /dev_pilot /api /fastapi_app.py
msaifee's picture
DevPilot
974e5e3
from fastapi import FastAPI, HTTPException, Depends, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import os
from dotenv import load_dotenv
from functools import lru_cache
from src.dev_pilot.LLMS.groqllm import GroqLLM
from src.dev_pilot.LLMS.geminillm import GeminiLLM
from src.dev_pilot.graph.graph_builder import GraphBuilder
from src.dev_pilot.graph.graph_executor import GraphExecutor
from src.dev_pilot.dto.sdlc_request import SDLCRequest
from src.dev_pilot.dto.sdlc_response import SDLCResponse
import uvicorn
from contextlib import asynccontextmanager
from src.dev_pilot.utils.logging_config import setup_logging
from loguru import logger
## Setup logging level
setup_logging(log_level="DEBUG")
gemini_models = [
"gemini-2.0-flash",
"gemini-2.0-flash-lite",
"gemini-2.5-pro-exp-03-25"
]
groq_models = [
"gemma2-9b-it",
"llama3-8b-8192",
"llama3-70b-8192"
]
def load_app():
uvicorn.run(app, host="0.0.0.0", port=8000)
class Settings:
def __init__(self):
self.GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
self.GROQ_API_KEY = os.getenv("GROQ_API_KEY")
@lru_cache()
def get_settings():
return Settings()
def validate_api_keys(settings: Settings = Depends(get_settings)):
required_keys = {
'GEMINI_API_KEY': settings.GEMINI_API_KEY,
'GROQ_API_KEY': settings.GROQ_API_KEY
}
missing_keys = [key for key, value in required_keys.items() if not value]
if missing_keys:
raise HTTPException(
status_code=500,
detail=f"Missing required API keys: {', '.join(missing_keys)}"
)
return settings
# Initialize the LLM and GraphBuilder instances once and store them in the app state
@asynccontextmanager
async def lifespan(app: FastAPI):
settings = get_settings()
llm = GeminiLLM(model=gemini_models[0], api_key=settings.GEMINI_API_KEY).get_llm_model()
graph_builder = GraphBuilder(llm=llm)
graph = graph_builder.setup_graph()
graph_executor = GraphExecutor(graph)
app.state.llm = llm
app.state.graph = graph
app.state.graph_executor = graph_executor
yield
# Clean up resources if needed
app.state.llm = None
app.state.graph = None
app.state.graph_executor = None
app = FastAPI(
title="DevPilot API",
description="AI-powered SDLC API using Langgraph",
version="1.0.0",
lifespan=lifespan
)
logger.info("Application starting up...")
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, replace with specific origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def root():
return {
"message": "Welcome to DevPilot API",
"docs_url": "/docs",
"redoc_url": "/redoc"
}
@app.post("/api/v1/sdlc/start", response_model=SDLCResponse)
async def start_sdlc(
sdlc_request: SDLCRequest,
settings: Settings = Depends(validate_api_keys)
):
try:
graph_executor = app.state.graph_executor
if isinstance (graph_executor, GraphExecutor) == False:
raise Exception("Graph Executor not initialized")
graph_response = graph_executor.start_workflow(sdlc_request.project_name)
logger.debug(f"Start Workflow Response: {graph_response}")
return SDLCResponse(
status="success",
message="SDLC process started successfully",
task_id=graph_response["task_id"],
state=graph_response["state"]
)
except Exception as e:
error_response = SDLCResponse(
status="error",
message="Failed to start the process",
error=str(e)
)
return JSONResponse(status_code=500, content=error_response.model_dump())
@app.post("/api/v1/sdlc/user_stories", response_model=SDLCResponse)
async def start_sdlc(
sdlc_request: SDLCRequest,
settings: Settings = Depends(validate_api_keys)
):
try:
graph_executor = app.state.graph_executor
if isinstance (graph_executor, GraphExecutor) == False:
raise Exception("Graph Executor not initialized")
graph_response = graph_executor.generate_stories(sdlc_request.task_id, sdlc_request.requirements)
logger.debug(f"Generate Stories Response: {graph_response}")
return SDLCResponse(
status="success",
message="User Stories generated successfully",
task_id=graph_response["task_id"],
state=graph_response["state"]
)
except Exception as e:
error_response = SDLCResponse(
status="error",
message="Failed to generate user stories",
error=str(e)
)
return JSONResponse(status_code=500, content=error_response.model_dump())
@app.post("/api/v1/sdlc/progress_flow", response_model=SDLCResponse)
async def progress_sdlc(
sdlc_request: SDLCRequest,
settings: Settings = Depends(validate_api_keys)
):
try:
graph_executor = app.state.graph_executor
if isinstance (graph_executor, GraphExecutor) == False:
raise Exception("Graph Executor not initialized")
graph_response = graph_executor.graph_review_flow(
sdlc_request.task_id,
sdlc_request.status,
sdlc_request.feedback,
sdlc_request.next_node)
logger.debug(f"Flow Node: {sdlc_request.next_node}")
logger.debug(f"Progress Flow Response: {graph_response}")
return SDLCResponse(
status="success",
message="Flow progressed successfully to next step",
task_id=graph_response["task_id"],
state=graph_response["state"]
)
except Exception as e:
error_response = SDLCResponse(
status="error",
message="Failed to progress the flow",
error=str(e)
)
return JSONResponse(status_code=500, content=error_response.model_dump())