Files
codeapi-new/main.py
yuetsh ebf541c1fe fix: 调试执行移入子进程并加上资源限制
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
2026-09-07 04:45:49 -06:00

162 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)