-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
160 lines (127 loc) · 3.69 KB
/
Copy pathapp.py
File metadata and controls
160 lines (127 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
from pathlib import Path
import traceback
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel, Field
from main import run_travel_agent, resume_travel_agent
# This is kept from the original project to allow the existing synchronous
# agent functions to call async MCP helpers inside FastAPI.
import nest_asyncio
nest_asyncio.apply()
BASE_DIR = Path(__file__).resolve().parent
app = FastAPI(
title="TripMate AI",
description=(
"LangGraph Multi-Agent Travel Planner with Supervisor, Guardrails, "
"Human-in-the-Loop, and FastAPI Frontend"
),
version="2.0.0",
)
app.mount(
"/static",
StaticFiles(directory=str(BASE_DIR / "static")),
name="static",
)
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
class TravelRequest(BaseModel):
message: str
thread_id: str | None = None
class ApprovalRequest(BaseModel):
thread_id: str = Field(min_length=1)
approved: bool
feedback: str = ""
@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
return templates.TemplateResponse(
request=request,
name="index.html",
context={},
)
@app.post("/api/travel")
async def travel_planner(request_data: TravelRequest):
try:
user_message = request_data.message.strip()
if not user_message:
return JSONResponse(
status_code=400,
content={
"success": False,
"error": "Message cannot be empty.",
},
)
result = run_travel_agent(
user_input=user_message,
thread_id=request_data.thread_id,
)
return JSONResponse(
content={
"success": True,
**result,
}
)
except Exception as exc:
print("ERROR:", exc)
traceback.print_exc()
return JSONResponse(
status_code=500,
content={
"success": False,
"error": str(exc),
},
)
@app.post("/api/travel/approve")
async def approve_travel_plan(request_data: ApprovalRequest):
try:
if not request_data.approved and not request_data.feedback.strip():
return JSONResponse(
status_code=400,
content={
"success": False,
"error": "Please provide revision feedback when rejecting the draft.",
},
)
result = resume_travel_agent(
thread_id=request_data.thread_id,
approved=request_data.approved,
feedback=request_data.feedback,
)
return JSONResponse(
content={
"success": True,
**result,
}
)
except Exception as exc:
print("APPROVAL ERROR:", exc)
traceback.print_exc()
return JSONResponse(
status_code=500,
content={
"success": False,
"error": str(exc),
},
)
@app.get("/health")
async def health_check():
return {
"status": "ok",
"message": "TripMate AI API is running",
"features": [
"supervisor_agent",
"input_guardrail",
"human_in_the_loop",
],
}
@app.get("/favicon.ico")
async def favicon():
return JSONResponse(content={})
if __name__ == "__main__":
uvicorn.run(
"app:app",
host="127.0.0.1",
port=8008,
reload=True,
)