Add tests, server, resume training, and project cleanup
- Add 57 unit tests covering generators, models, and pipeline components - Implement FastAPI HTTP service (server.py) with POST /solve and GET /health - Add checkpoint resume (断点续训) to both CTC and regression training utils - Fix device mismatch bug in CTC training (targets/input_lengths on GPU) - Add pytest dev dependency to pyproject.toml - Update .gitignore with data/solver/, data/real/, *.log - Remove PyCharm template main.py - Update training/__init__.py docs for solver training scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
88
server.py
Normal file
88
server.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
FastAPI HTTP 推理服务
|
||||
|
||||
提供 REST API 识别验证码:
|
||||
POST /solve - 上传图片,返回识别结果
|
||||
GET /health - 健康检查
|
||||
|
||||
启动方式:
|
||||
uv run python cli.py serve --port 8080
|
||||
|
||||
请求示例:
|
||||
curl -X POST http://localhost:8080/solve -F "image=@captcha.png"
|
||||
|
||||
响应示例:
|
||||
{"type": "normal", "result": "A3B8", "confidence": 0.95, "time_ms": 45}
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def create_app():
|
||||
"""
|
||||
创建 FastAPI 应用实例(工厂函数)。
|
||||
|
||||
cli.py 的 cmd_serve 依赖此签名。
|
||||
需要安装 server 可选依赖: uv sync --extra server
|
||||
"""
|
||||
from fastapi import FastAPI, File, Query, UploadFile
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from inference.pipeline import CaptchaPipeline
|
||||
|
||||
app = FastAPI(
|
||||
title="CaptchaBreaker",
|
||||
description="验证码识别多模型系统 - HTTP 推理服务",
|
||||
version="0.1.0",
|
||||
)
|
||||
|
||||
pipeline: CaptchaPipeline | None = None
|
||||
|
||||
@app.on_event("startup")
|
||||
def _load_models():
|
||||
nonlocal pipeline
|
||||
try:
|
||||
pipeline = CaptchaPipeline()
|
||||
except FileNotFoundError:
|
||||
# 模型未导出时允许启动,但 /solve 会返回 503
|
||||
pipeline = None
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
models_loaded = pipeline is not None
|
||||
return {"status": "ok" if models_loaded else "no_models", "models_loaded": models_loaded}
|
||||
|
||||
@app.post("/solve")
|
||||
async def solve(
|
||||
image: UploadFile = File(...),
|
||||
type: str | None = Query(None, description="指定类型跳过分类"),
|
||||
):
|
||||
if pipeline is None:
|
||||
return JSONResponse(
|
||||
status_code=503,
|
||||
content={"error": "模型未加载,请先训练并导出 ONNX 模型"},
|
||||
)
|
||||
|
||||
data = await image.read()
|
||||
if not data:
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={"error": "空文件"},
|
||||
)
|
||||
|
||||
try:
|
||||
result = pipeline.solve(data, captcha_type=type)
|
||||
except RuntimeError as e:
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={"error": str(e)},
|
||||
)
|
||||
|
||||
return {
|
||||
"type": result["type"],
|
||||
"result": result["result"],
|
||||
"raw": result["raw"],
|
||||
"time_ms": result["time_ms"],
|
||||
}
|
||||
|
||||
return app
|
||||
Reference in New Issue
Block a user