pg_logger 原来直接在 API 进程内跑用户代码,有三个问题: - /debug 是 async def 却做同步的 CPU 密集跟踪,单个请求会堵死事件循环 - exec_script_str_local 传了 disable_security_checks=True,setrlimit 被跳过, MAX_EXECUTED_LINES 只能限制行事件数,挡不住 sum(range(10**9)) 这类单行重计算 - input_string_queue 和 sys.stdout 都是模块级全局,并发请求会互相干扰 改成在子进程里执行:子进程内设 CPU 5s / 内存 512MB(软硬限留差值, 让 SIGXCPU 先于 SIGKILL 到达以便区分原因),父进程再加 15s 墙钟超时兜住 time.sleep 这类不耗 CPU 的挂起。端点改同步 def 走线程池,异常统一转成 400 + 中文 detail。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QGZaLUWzGPEMTk6A5RShC
162 lines
4.7 KiB
Python
162 lines
4.7 KiB
Python
from io import StringIO
|
||
from fastapi import FastAPI, HTTPException
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
from fastapi.responses import StreamingResponse
|
||
import os
|
||
import json
|
||
from openai import OpenAI
|
||
from schemas import (
|
||
PresetCodeCreate,
|
||
AIAnalysisRequest,
|
||
DebugRequest,
|
||
FormatRequest,
|
||
FormatResponse,
|
||
)
|
||
from database import DatabaseService
|
||
from debug_runner import DebugError, run_debug
|
||
from formatter import format_code, FormatError
|
||
from dotenv import load_dotenv
|
||
|
||
|
||
# 加载环境变量
|
||
load_dotenv()
|
||
|
||
app = FastAPI(title="Code API", version="1.0.0")
|
||
|
||
# CORS 配置
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=[
|
||
"https://code.xuyue.cc",
|
||
"http://10.13.114.114",
|
||
"http://localhost:3000",
|
||
],
|
||
allow_credentials=True,
|
||
allow_methods=["*"],
|
||
allow_headers=["*"],
|
||
)
|
||
|
||
# 数据库配置
|
||
DATABASE_URL = "sqlite://database.db"
|
||
|
||
# 初始化数据库
|
||
DatabaseService.init_database(app, DATABASE_URL)
|
||
|
||
|
||
@app.get("/")
|
||
async def get_all_codes() -> dict:
|
||
"""获取所有预设代码"""
|
||
codes = await DatabaseService.get_all_codes()
|
||
return {"data": codes}
|
||
|
||
|
||
@app.get("/query/{query}")
|
||
async def get_code_by_query(query: str) -> dict:
|
||
"""根据查询字符串获取特定代码"""
|
||
code = await DatabaseService.get_code_by_query(query)
|
||
if not code:
|
||
raise HTTPException(status_code=404, detail="Record not found!")
|
||
return {"data": code}
|
||
|
||
|
||
@app.post("/")
|
||
async def create_code(code_data: PresetCodeCreate) -> dict:
|
||
"""创建新的预设代码"""
|
||
try:
|
||
code = await DatabaseService.create_code(code_data)
|
||
return {"data": code}
|
||
except Exception as e:
|
||
raise HTTPException(status_code=400, detail=str(e))
|
||
|
||
|
||
@app.delete("/{code_id}")
|
||
async def delete_code(code_id: int) -> dict:
|
||
"""删除指定 ID 的代码"""
|
||
success = await DatabaseService.delete_code(code_id)
|
||
if not success:
|
||
raise HTTPException(status_code=400, detail="Record not found!")
|
||
|
||
return {"data": True}
|
||
|
||
|
||
@app.post("/ai")
|
||
async def ai_analysis(request: AIAnalysisRequest):
|
||
"""AI 代码分析端点"""
|
||
code = request.code
|
||
error_info = request.error_info
|
||
language = request.language
|
||
|
||
api_key = os.getenv("API_KEY")
|
||
if not api_key:
|
||
raise HTTPException(status_code=400, detail="API_KEY is not set")
|
||
|
||
system_prompt = "你是编程老师,擅长分析代码和错误信息,一般出错在语法和格式,请指出错误在第几行,并给出中文的、简要的解决方法。用 markdown 格式返回。"
|
||
user_prompt = f"编程语言:{language}\n代码:\n```{language}\n{code}\n```\n错误信息:\n```\n{error_info}\n```"
|
||
|
||
def generate_response():
|
||
try:
|
||
# 初始化 OpenAI 客户端
|
||
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
|
||
|
||
# 创建流式响应
|
||
stream = client.chat.completions.create(
|
||
model="deepseek-chat",
|
||
messages=[
|
||
{"role": "system", "content": system_prompt},
|
||
{"role": "user", "content": user_prompt},
|
||
],
|
||
stream=True,
|
||
seed=0,
|
||
)
|
||
|
||
for chunk in stream:
|
||
if chunk.choices and len(chunk.choices) > 0:
|
||
delta = chunk.choices[0].delta
|
||
if hasattr(delta, "content") and delta.content:
|
||
yield f"data: {json.dumps({'event': 'chunk', 'data': delta.content})}\n\n"
|
||
|
||
# 发送完成信号
|
||
yield f"data: {json.dumps({'event': 'done', 'data': ''})}\n\n"
|
||
|
||
except Exception as e:
|
||
yield f"data: {json.dumps({'event': 'error', 'data': str(e)})}\n\n"
|
||
|
||
return StreamingResponse(
|
||
generate_response(),
|
||
media_type="text/event-stream",
|
||
headers={
|
||
"Cache-Control": "no-cache",
|
||
"Connection": "keep-alive",
|
||
},
|
||
)
|
||
|
||
|
||
@app.post("/debug")
|
||
def debug(request: DebugRequest) -> dict:
|
||
"""调试端点
|
||
|
||
用同步 def 而不是 async def:跟踪执行是 CPU 密集的阻塞调用,
|
||
交给 FastAPI 的线程池,避免堵死事件循环。
|
||
"""
|
||
try:
|
||
data = run_debug(request.code, request.inputs)
|
||
except DebugError as e:
|
||
raise HTTPException(status_code=400, detail=str(e))
|
||
return {"data": data}
|
||
|
||
|
||
@app.post("/format", response_model=FormatResponse)
|
||
async def format_code_endpoint(request: FormatRequest) -> FormatResponse:
|
||
"""格式化代码"""
|
||
try:
|
||
formatted = format_code(request.code, request.language)
|
||
except FormatError as e:
|
||
raise HTTPException(status_code=400, detail=str(e))
|
||
return FormatResponse(code=formatted)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import uvicorn
|
||
|
||
uvicorn.run(app, host="0.0.0.0", port=8080)
|