Emmanuel Cheruiyot's picture

Emmanuel Cheruiyot

Holy-Trinity-Tower-of-God
1 1
·

AI & ML interests

Project Setup & Requirements First, install the necessary dependencies: pip install fastapi uvicorn langgraph langchain-openai pydantic sse-starlette python-dotenv Set your API key in an .env file or environment variable: export OPENAI_API_KEY="your-api-key-here" File 1: agent.py (LangGraph Definition) This module defines the agent's state, tools, graph nodes, and workflow compilation. import os from typing import Annotated, TypedDict, Sequence from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.checkpoint.memory import MemorySaver # 1. Define Agent State class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], add_messages] # 2. Initialize LLM Model # Requires OPENAI_API_KEY in environment llm = ChatOpenAI(model="gpt-4o-mini", streaming=True) # 3. Define Graph Nodes async def call_model_node(state: AgentState): """Executes the primary model call with system prompt guidance.""" messages = state["messages"] system_prompt = SystemMessage( content="You are a helpful, concise AI assistant integrated into a FastAPI web backend." ) # Prepend system message if not present if not any(isinstance(m, SystemMessage) for m in messages): messages = [system_prompt] + list(messages) response = await llm.ainvoke(messages) return {"messages": [response]} # 4. Construct Graph builder = StateGraph(AgentState) # Add node(s) builder.add_node("agent", call_model_node) # Add edge(s) builder.add_edge(START, "agent") builder.add_edge("agent", END) # In-memory checkpointer for thread history (Use PostgresSaver for production) checkpointer = MemorySaver() # Compile the workflow agent_graph = builder.compile(checkpointer=checkpointer) File 2: main.py (FastAPI Server) This module initializes FastAPI, sets up CORS middleware, and provides both standard JSON and real-time streaming (SSE) endpoints. import json import asyncio from typing import AsyncGenerator, List, Optional from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from sse_starlette.sse import EventSourceResponse from langchain_core.messages import HumanMessage, AIMessage from agent import agent_graph app = FastAPI( title="LangGraph + FastAPI Integration Starter", version="1.0.0", ) # Enable CORS for local web clients app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Request & Response Schemas class ChatRequest(BaseModel): message: str = Field(..., description="User message input") thread_id: str = Field(default="default_thread", description="Unique session identifier for history tracking") class ChatResponse(BaseModel): thread_id: str response: str # ------------------------------------------------------------------ # Endpoint 1: Synchronous / Standard JSON Endpoint # ------------------------------------------------------------------ @app.post("/api/chat", response_model=ChatResponse) async def chat_endpoint(request: ChatRequest): """Executes graph and returns complete response in standard JSON.""" config = {"configurable": {"thread_id": request.thread_id}} inputs = {"messages": [HumanMessage(content=request.message)]} try: # Run graph to completion result = await agent_graph.ainvoke(inputs, config=config) last_message = result["messages"][-1] return ChatResponse( thread_id=request.thread_id, response=str(last_message.content) ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # ------------------------------------------------------------------ # Endpoint 2: Streaming SSE (Server-Sent Events) Endpoint # ------------------------------------------------------------------ async def generate_graph_stream(message: str, thread_id: str) -> AsyncGenerator[dict, None]: """Streams token events directly from the LangGraph execution pipeline.""" config = {"configurable": {"thread_id": thread_id}} inputs = {"messages": [HumanMessage(content=message)]} try: # Stream events from LangGraph async for event in agent_graph.astream_events(inputs, config=config, version="v2"): # Intercept raw tokens generated by the LLM inside the graph node if event["event"] == "on_chat_model_stream": chunk_content = event["data"]["chunk"].content if chunk_content: yield { "event": "message", "data": json.dumps({"content": chunk_content}) } # Send end marker event yield {"event": "done", "data": json.dumps({"status": "completed"})} except Exception as e: yield {"event": "error", "data": json.dumps({"error": str(e)})} @app.post("/api/chat/stream") async def stream_chat_endpoint(request: ChatRequest): """Streams response tokens line-by-line via SSE.""" return EventSourceResponse( generate_graph_stream(request.message, request.thread_id) ) # Health Check @app.get("/health") async def health_check(): return {"status": "ok"} if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) How to Run & Test * Start the FastAPI Server: python main.py The interactive API docs will be live at http://localhost:8000/docs. * Test standard JSON Endpoint via curl: curl -X POST "http://localhost:8000/api/chat" \ -H "Content-Type: application/json" \ -d '{"message": "Hello, how does this architecture work?", "thread_id": "user_session_1"}' * Test Real-Time Streaming (SSE) via curl: curl -N -X POST "http://localhost:8000/api/chat/stream" \ -H "Content-Type: application/json" \ -d '{"message": "Write a short poem about building AI apps.", "thread_id": "user_session_1"}'

Recent Activity

Organizations

Stratera Stack 's profile picture