-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathapp.py
More file actions
148 lines (127 loc) · 4.79 KB
/
app.py
File metadata and controls
148 lines (127 loc) · 4.79 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
import warnings
warnings.filterwarnings("ignore", message="resource_tracker: There appear to be.*")
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from pydantic import BaseModel
from typing import List, Optional
import os
import logging
import anthropic
logger = logging.getLogger(__name__)
from config import config
from rag_system import RAGSystem
# Initialize FastAPI app
app = FastAPI(title="Course Materials RAG System", root_path="")
# Add trusted host middleware for proxy
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["*"]
)
# Enable CORS with proper settings for proxy
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
# Initialize RAG system
rag_system = RAGSystem(config)
# Pydantic models for request/response
class QueryRequest(BaseModel):
"""Request model for course queries"""
query: str
session_id: Optional[str] = None
class QueryResponse(BaseModel):
"""Response model for course queries"""
answer: str
sources: List[str]
session_id: str
class CourseStats(BaseModel):
"""Response model for course statistics"""
total_courses: int
course_titles: List[str]
# API Endpoints
@app.post("/api/query", response_model=QueryResponse)
async def query_documents(request: QueryRequest):
"""Process a query and return response with sources"""
try:
# Create session if not provided
session_id = request.session_id
if not session_id:
session_id = rag_system.session_manager.create_session()
# Process query using RAG system
answer, sources = rag_system.query(request.query, session_id)
return QueryResponse(
answer=answer,
sources=sources,
session_id=session_id
)
except anthropic.AuthenticationError as e:
logger.error(f"Anthropic API authentication failed: {e}")
raise HTTPException(
status_code=503,
detail="AI service authentication failed. Please check that ANTHROPIC_API_KEY is set correctly in the .env file."
)
except anthropic.RateLimitError as e:
logger.error(f"Anthropic API rate limit: {e}")
raise HTTPException(
status_code=429,
detail="AI service rate limit reached. Please try again in a moment."
)
except anthropic.APIConnectionError as e:
logger.error(f"Anthropic API connection error: {e}")
raise HTTPException(
status_code=503,
detail="Could not connect to AI service. Please check your internet connection."
)
except anthropic.APIError as e:
logger.error(f"Anthropic API error: {e}")
raise HTTPException(
status_code=502,
detail=f"AI service error: {str(e)}"
)
except Exception as e:
logger.error(f"Unexpected error processing query: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}")
@app.get("/api/courses", response_model=CourseStats)
async def get_course_stats():
"""Get course analytics and statistics"""
try:
analytics = rag_system.get_course_analytics()
return CourseStats(
total_courses=analytics["total_courses"],
course_titles=analytics["course_titles"]
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.on_event("startup")
async def startup_event():
"""Load initial documents on startup"""
docs_path = "../docs"
if os.path.exists(docs_path):
print("Loading initial documents...")
try:
courses, chunks = rag_system.add_course_folder(docs_path, clear_existing=False)
print(f"Loaded {courses} courses with {chunks} chunks")
except Exception as e:
print(f"Error loading documents: {e}")
# Custom static file handler with no-cache headers for development
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import os
from pathlib import Path
class DevStaticFiles(StaticFiles):
async def get_response(self, path: str, scope):
response = await super().get_response(path, scope)
if isinstance(response, FileResponse):
# Add no-cache headers for development
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
return response
# Serve static files for the frontend
app.mount("/", StaticFiles(directory="../frontend", html=True), name="static")