# -*- coding: utf-8 -*- """ AI Free Chat - Flask 主应用 提供聊天页面和 SSE 流式接口 """ import os import json from flask import Flask, render_template, request, Response, stream_with_context from dotenv import load_dotenv from services.doubao import DoubaoService from services.deepseek import DeepSeekService # 加载环境变量 load_dotenv() app = Flask(__name__) # 初始化服务 services = { "doubao": DoubaoService(), "deepseek": DeepSeekService(), } @app.route("/") def index(): """渲染聊天页面""" return render_template("index.html") @app.route("/api/chat", methods=["POST"]) def chat(): """ SSE 流式聊天接口 请求体: { provider: 'doubao'|'deepseek', messages: [{role, content}] } 响应: text/event-stream """ data = request.get_json(silent=True) or {} provider = data.get("provider", "deepseek") messages = data.get("messages", []) if provider not in services: return Response( f"data: {json.dumps({'error': '未知的 provider'})}\n\n", mimetype="text/event-stream", ) if not messages: return Response( f"data: {json.dumps({'error': 'messages 不能为空'})}\n\n", mimetype="text/event-stream", ) service = services[provider] def generate(): """生成 SSE 事件流""" try: for chunk in service.stream_chat(messages): payload = json.dumps({"content": chunk}, ensure_ascii=False) yield f"data: {payload}\n\n" yield "data: [DONE]\n\n" except Exception as e: # noqa: BLE001 err = json.dumps({"error": str(e)}, ensure_ascii=False) yield f"data: {err}\n\n" headers = { "Cache-Control": "no-cache", "X-Accel-Buffering": "no", "Connection": "keep-alive", } return Response( stream_with_context(generate()), mimetype="text/event-stream", headers=headers, ) if __name__ == "__main__": port = int(os.getenv("PORT", "5000")) app.run(host="0.0.0.0", port=port, debug=True, threaded=True)