Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 242 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import os
import asyncio
import json
from threading import Thread
from flask import Flask, render_template, jsonify, request, send_file, Response
from flask_cors import CORS
from flask_socketio import SocketIO, emit
from datetime import datetime
from config import Config, logger
from async_discussion import AsyncDiscussionManager
from history_manager import HistoryManager

app = Flask(__name__,
static_folder='static',
template_folder='templates')
app.config.from_object(Config)

CORS(app, resources={r"/api/*": {"origins": "*"}})
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')

discussion_manager: AsyncDiscussionManager = None
history_manager = HistoryManager()

active_discussions = {}


def progress_callback(event_data):
socketio.emit('discussion_update', event_data)


@app.route('/')
def index():
return render_template('index.html')


@app.route('/api/status')
def get_status():
env_status = Config.check_environment()
return jsonify({
"status": env_status["status"],
"environment": env_status["config"],
"errors": env_status["errors"],
"warnings": env_status["warnings"]
})


@app.route('/api/environment', methods=['GET'])
def get_environment():
env_status = Config.check_environment()
return jsonify(env_status)


@app.route('/api/discussion/start', methods=['POST'])
def start_discussion():
global discussion_manager

data = request.get_json()
problem = data.get('problem', '')
max_rounds = data.get('max_rounds', Config.MAX_ROUNDS)

if not problem.strip():
return jsonify({"error": "请输入讨论问题"}), 400

env_status = Config.check_environment()
if len(env_status["errors"]) > 0:
return jsonify({
"error": "环境配置错误",
"details": env_status["errors"]
}), 400

discussion_manager = AsyncDiscussionManager(on_progress_callback=progress_callback)

def run_discussion():
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
result = loop.run_until_complete(
discussion_manager.start_discussion(problem, max_rounds)
)
loop.close()
except Exception as e:
logger.error(f"讨论线程出错: {str(e)}", exc_info=True)
socketio.emit('discussion_error', {"message": str(e)})

thread = Thread(target=run_discussion, daemon=True)
thread.start()

return jsonify({
"status": "started",
"message": "讨论已开始",
"problem": problem,
"max_rounds": max_rounds
})


@app.route('/api/discussion/stop', methods=['POST'])
def stop_discussion():
global discussion_manager

if not discussion_manager or not discussion_manager.is_running:
return jsonify({"error": "没有正在进行的讨论"}), 400

discussion_manager.stop_discussion()

return jsonify({
"status": "stopped",
"message": "讨论已停止"
})


@app.route('/api/discussion/current')
def get_current_discussion():
global discussion_manager

if not discussion_manager:
return jsonify({"session": None})

session = discussion_manager.get_current_session()
return jsonify({"session": session})


@app.route('/api/history')
def get_history():
limit = request.args.get('limit', type=int)
sessions = history_manager.list_all_sessions(limit=limit)
return jsonify({
"sessions": sessions,
"total": len(sessions)
})


@app.route('/api/history/<filename>')
def get_history_session(filename):
session = history_manager.get_session(filename)
if not session:
return jsonify({"error": "会话不存在"}), 404

return jsonify(session)


@app.route('/api/history/<filename>/delete', methods=['DELETE'])
def delete_history_session(filename):
success = history_manager.delete_session(filename)
if not success:
return jsonify({"error": "删除失败"}), 400

return jsonify({"status": "deleted", "message": "会话已删除"})


@app.route('/api/history/<filename>/export')
def export_history_session(filename):
markdown = history_manager.export_session_to_markdown(filename)
if not markdown:
return jsonify({"error": "导出失败"}), 400

timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
export_filename = f"discussion_report_{timestamp}.md"

return Response(
markdown,
mimetype='text/markdown',
headers={
'Content-Disposition': f'attachment; filename="{export_filename}"'
}
)


@app.route('/api/history/<filename>/download')
def download_history_session(filename):
session = history_manager.get_session(filename)
if not session:
return jsonify({"error": "文件不存在"}), 404

filepath = os.path.join(Config.OUTPUT_DIR, filename)
if not os.path.exists(filepath):
filepath = filename
if not os.path.exists(filepath):
return jsonify({"error": "文件不存在"}), 404

return send_file(
filepath,
mimetype='application/json',
as_attachment=True,
download_name=filename
)


@app.route('/api/statistics')
def get_statistics():
stats = history_manager.get_statistics()
return jsonify(stats)


@socketio.on('connect')
def handle_connect():
logger.info(f"客户端连接: {request.sid}")
emit('connected', {"status": "ok", "sid": request.sid})


@socketio.on('disconnect')
def handle_disconnect():
logger.info(f"客户端断开连接: {request.sid}")


@socketio.on('ping')
def handle_ping():
emit('pong', {"timestamp": datetime.now().isoformat()})


def ensure_directories():
dirs = [
Config.OUTPUT_DIR,
'static',
'static/css',
'static/js',
'templates'
]
for d in dirs:
if not os.path.exists(d):
os.makedirs(d, exist_ok=True)
logger.info(f"创建目录: {d}")


if __name__ == '__main__':
ensure_directories()

env_status = Config.check_environment()
if len(env_status["errors"]) > 0:
logger.warning("环境配置存在错误:")
for error in env_status["errors"]:
logger.warning(f" - {error}")
else:
logger.info("环境配置检查通过")

logger.info(f"启动Web服务,调试模式: {Config.DEBUG}")
socketio.run(
app,
host='0.0.0.0',
port=5000,
debug=Config.DEBUG,
allow_unsafe_werkzeug=True
)
Loading