181 lines
6.3 KiB
Python
181 lines
6.3 KiB
Python
"""智能问答路由(A2)
|
||
|
||
POST /chat/message 发送消息(SSE 流式)
|
||
GET /chat/sessions 会话列表
|
||
POST /chat/sessions 创建会话
|
||
DELETE /chat/sessions/{id} 删除会话
|
||
GET /chat/sessions/{id}/messages 消息记录
|
||
POST /chat/messages/{id}/feedback 提交反馈
|
||
POST /chat/suggest 推荐追问
|
||
GET /chat/app-opening 获取应用开场配置
|
||
"""
|
||
import json
|
||
from flask import Blueprint, request, jsonify, Response, current_app
|
||
from insurance.chat.service import ChatService
|
||
from insurance.middleware.auth_middleware import jwt_required
|
||
|
||
chat_bp = Blueprint("chat", __name__)
|
||
chat_service = ChatService()
|
||
|
||
|
||
@chat_bp.route("/message", methods=["POST"])
|
||
@jwt_required
|
||
def send_message():
|
||
"""发送消息,SSE 流式返回 AI 回答。"""
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({"code": 1001, "message": "请求体不能为空", "data": None}), 400
|
||
message = data.get("message", "")
|
||
session_id = data.get("session_id", "")
|
||
filters = data.get("filters", {})
|
||
api_token = data.get("api_token", "")
|
||
|
||
if not message:
|
||
return jsonify({"code": 1001, "message": "请输入问题内容", "data": None}), 400
|
||
|
||
user_id = request.user_id
|
||
|
||
# 在请求上下文内提前读取配置和捕获 app 对象(生成器运行时上下文已销毁)
|
||
default_api_key = current_app.config.get("BAODAN_CHAT_API_KEY", "")
|
||
base_url = current_app.config.get("BAODAN_API_URL", "http://localhost:5001")
|
||
flask_app = current_app._get_current_object()
|
||
|
||
def generate():
|
||
# 在整个生成器执行期间保持应用上下文
|
||
with flask_app.app_context():
|
||
try:
|
||
for event in chat_service.send_message_stream(
|
||
user_id=user_id,
|
||
message=message,
|
||
session_id=session_id,
|
||
filters=filters,
|
||
api_token=api_token or default_api_key,
|
||
base_url=base_url,
|
||
):
|
||
yield f"data: {event}\n\n"
|
||
except GeneratorExit:
|
||
# 客户端断开连接,静默退出
|
||
pass
|
||
except Exception as e:
|
||
import logging
|
||
logging.exception(f"SSE 生成器异常: {e}")
|
||
yield f"data: {json.dumps({'type': 'error', 'data': {'message': '服务器内部错误'}})}\n\n"
|
||
|
||
response = Response(generate(), content_type="text/event-stream")
|
||
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
||
response.headers["X-Accel-Buffering"] = "no"
|
||
response.headers["Connection"] = "keep-alive"
|
||
return response
|
||
|
||
|
||
@chat_bp.route("/sessions", methods=["GET"])
|
||
@jwt_required
|
||
def get_sessions():
|
||
"""获取当前用户的会话列表。"""
|
||
page = max(1, request.args.get("page", 1, type=int))
|
||
page_size = min(100, max(1, request.args.get("page_size", 20, type=int)))
|
||
user_id = request.user_id
|
||
result = chat_service.get_sessions(user_id, page, page_size)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/sessions", methods=["POST"])
|
||
@jwt_required
|
||
def create_session():
|
||
"""创建新会话。"""
|
||
user_id = request.user_id
|
||
result = chat_service.create_session(user_id)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/sessions/<session_id>", methods=["DELETE"])
|
||
@jwt_required
|
||
def delete_session(session_id):
|
||
"""软删除会话。"""
|
||
user_id = request.user_id
|
||
result = chat_service.delete_session(user_id, session_id)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/sessions/<session_id>/messages", methods=["GET"])
|
||
@jwt_required
|
||
def get_messages(session_id):
|
||
"""获取会话消息记录。"""
|
||
user_id = request.user_id
|
||
result = chat_service.get_messages(user_id, session_id)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/messages/<message_id>/feedback", methods=["POST"])
|
||
@jwt_required
|
||
def submit_feedback(message_id):
|
||
"""提交回答评分或纠错。"""
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({"code": 1001, "message": "请求体不能为空", "data": None}), 400
|
||
rating = data.get("rating", "")
|
||
correction = data.get("correction", "")
|
||
user_id = request.user_id
|
||
|
||
if rating not in ("helpful", "not_helpful"):
|
||
return jsonify({"code": 1001, "message": "评分值无效", "data": None}), 400
|
||
|
||
result = chat_service.submit_feedback(user_id, message_id, rating, correction)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/suggest", methods=["POST"])
|
||
@jwt_required
|
||
def suggest():
|
||
"""基于当前回答生成推荐追问。"""
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({"code": 1001, "message": "请求体不能为空", "data": None}), 400
|
||
message = data.get("message", "")
|
||
if not message:
|
||
return jsonify({"code": 1001, "message": "请输入回答内容", "data": None}), 400
|
||
|
||
result = chat_service.get_suggestions(message)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/app-opening", methods=["GET"])
|
||
@jwt_required
|
||
def get_app_opening():
|
||
"""获取 Dify 应用的开场配置(开场白 + 推荐问题)。"""
|
||
api_token = request.args.get("api_token", "")
|
||
result = chat_service.get_app_opening(api_token)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/sessions/<session_id>/rename", methods=["PUT"])
|
||
@jwt_required
|
||
def rename_session(session_id):
|
||
"""重命名会话。"""
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({"code": 1001, "message": "请求体不能为空", "data": None}), 400
|
||
name = data.get("name", "")
|
||
if not name:
|
||
return jsonify({"code": 1001, "message": "请输入会话名称", "data": None}), 400
|
||
|
||
user_id = request.user_id
|
||
result = chat_service.rename_session(user_id, session_id, name)
|
||
return jsonify(result)
|
||
|
||
|
||
@chat_bp.route("/sessions/<session_id>/auto-name", methods=["POST"])
|
||
@jwt_required
|
||
def auto_name_session(session_id):
|
||
"""根据首条消息自动命名会话。"""
|
||
data = request.get_json()
|
||
if not data:
|
||
return jsonify({"code": 1001, "message": "请求体不能为空", "data": None}), 400
|
||
first_message = data.get("message", "")
|
||
if not first_message:
|
||
return jsonify({"code": 1001, "message": "请输入消息内容", "data": None}), 400
|
||
|
||
user_id = request.user_id
|
||
result = chat_service.auto_name_session(user_id, session_id, first_message)
|
||
return jsonify(result)
|