-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
119 lines (95 loc) · 3.7 KB
/
Copy pathapp.py
File metadata and controls
119 lines (95 loc) · 3.7 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
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, StreamingResponse
from pydantic import BaseModel
import uuid
import logging
import json
from retrieval import retrieve
from generation import generate_answer, generate_answer_stream, ConversationManager
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
app = FastAPI(title="PolyU Smart Course Advisor API")
# Serve static files (HTML, CSS, JS)
app.mount("/static", StaticFiles(directory="static"), name="static")
conversation_manager = ConversationManager()
class QuestionRequest(BaseModel):
question: str
session_id: str = ""
class AnswerResponse(BaseModel):
answer: str
sources: list[dict]
intent: str
session_id: str
@app.get("/")
async def root():
return FileResponse("static/index.html")
@app.post("/api/ask")
async def ask(req: QuestionRequest):
session_id = req.session_id or str(uuid.uuid4())
# 1. Retrieve history
history = conversation_manager.get_history(session_id)
# 2. Execute retrieval (async)
logging.info(f"Processing question for session {session_id}: {req.question}")
retrieval_result = await retrieve(req.question)
# 3. Generate answer
answer, sources = generate_answer(req.question, retrieval_result, history)
# 4. Update history
conversation_manager.add_message(session_id, "user", req.question)
conversation_manager.add_message(session_id, "assistant", answer)
return AnswerResponse(
answer=answer,
sources=sources,
intent=retrieval_result["intent"],
session_id=session_id
)
@app.post("/api/ask/stream")
async def ask_stream(req: QuestionRequest):
session_id = req.session_id or str(uuid.uuid4())
history = conversation_manager.get_history(session_id)
retrieval_result = await retrieve(req.question)
token_gen, sources = generate_answer_stream(
req.question, retrieval_result, history
)
# 记录对话历史需要收集完整回答
# 方案:在 stream 结束后通过前端回传,或在后端收集
async def event_stream():
# 第一条 SSE:发送 metadata(sources、intent、session_id)
meta = {
"type": "meta",
"sources": sources,
"intent": retrieval_result["intent"],
"session_id": session_id
}
yield f"data: {json.dumps(meta, ensure_ascii=False)}\n\n"
# 逐 token 推送
full_answer = []
for token in token_gen:
full_answer.append(token)
payload = {"type": "token", "content": token}
yield f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
# 最后一条 SSE:发送完成信号
yield f"data: {json.dumps({'type': 'done'})}\n\n"
# 流结束后保存对话历史
complete_answer = "".join(full_answer)
conversation_manager.add_message(session_id, "user", req.question)
conversation_manager.add_message(session_id, "assistant", complete_answer)
return StreamingResponse(
event_stream(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no", # 防止 nginx 缓冲
}
)
@app.post("/api/clear")
async def clear_session(req: dict):
session_id = req.get("session_id", "")
if session_id:
conversation_manager.clear(session_id)
logging.info(f"Cleared session: {session_id}")
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="127.0.0.1", port=8080, reload=True)