from typing import Any, Literal from fastapi import FastAPI from langchain.agents import create_agent from langchain.tools import tool from langchain_core.language_models.fake_chat_models import FakeMessagesListChatModel from langchain_core.messages import AIMessage, ToolMessage from pydantic import BaseModel, Field class WebhookEvent(BaseModel): source: str = Field(min_length=2) event_type: Literal["ticket.created", "incident.alert", "audit.recorded"] payload: dict[str, Any] = Field(default_factory=dict) class RouterResult(BaseModel): route: str tool: str tool_output: str agent_reply: str @tool def summarize_ticket(title: str, priority: str = "normal") -> str: """Create a support ticket routing summary.""" return f"support ticket routed: {title} ({priority})" @tool def escalate_incident(service: str, severity: str) -> str: """Create an incident escalation note.""" return f"incident escalation queued for {service} at {severity} severity" @tool def record_audit_event(action: str, actor: str) -> str: """Record a security audit event.""" return f"audit event recorded: {actor} {action}" TOOLS = [summarize_ticket, escalate_incident, record_audit_event] class RoutingDemoModel(FakeMessagesListChatModel): def bind_tools(self, tools, **kwargs): return self def route_for(event: WebhookEvent) -> dict[str, Any]: if event.event_type == "ticket.created": return { "name": "support_triage", "tool": "summarize_ticket", "args": { "title": str(event.payload.get("title", "Untitled ticket")), "priority": str(event.payload.get("priority", "normal")), }, } if event.event_type == "incident.alert": return { "name": "incident_escalation", "tool": "escalate_incident", "args": { "service": str(event.payload.get("service", "unknown-service")), "severity": str(event.payload.get("severity", "warning")), }, } return { "name": "audit_log", "tool": "record_audit_event", "args": { "action": str(event.payload.get("action", "unknown-action")), "actor": str(event.payload.get("actor", event.source)), }, } def invoke_routed_agent(event: WebhookEvent) -> RouterResult: route = route_for(event) model = RoutingDemoModel( responses=[ AIMessage( content="", tool_calls=[ { "id": f"call_{route['tool']}", "name": route["tool"], "args": route["args"], } ], ), AIMessage( content=( f"Agent route {route['name']} completed for " f"{event.event_type}." ) ), ] ) agent = create_agent( model=model, tools=TOOLS, system_prompt="Route webhook events to the matching operational tool.", ) result = agent.invoke( { "messages": [ { "role": "user", "content": ( f"Handle {event.event_type} from {event.source} " f"with payload {event.payload}." ), } ] } ) tool_output = next( message.content for message in result["messages"] if isinstance(message, ToolMessage) ) return RouterResult( route=route["name"], tool=route["tool"], tool_output=tool_output, agent_reply=result["messages"][-1].content, ) app = FastAPI(title="LangChain Webhook Agent Router") @app.get("/health") def health() -> dict[str, str]: return {"status": "ok"} @app.post("/webhooks/events", response_model=RouterResult) def handle_webhook(event: WebhookEvent) -> RouterResult: return invoke_routed_agent(event)