133 lines
5.8 KiB
Python
133 lines
5.8 KiB
Python
|
from fastapi import APIRouter, Request, HTTPException
|
|||
|
import json
|
|||
|
import logging
|
|||
|
|
|||
|
from Util import OcrUtil, LlmUtil
|
|||
|
from sse_starlette.sse import EventSourceResponse
|
|||
|
|
|||
|
# 创建路由路由器
|
|||
|
router = APIRouter(prefix="/api", tags=["作文批阅"])
|
|||
|
|
|||
|
# 配置日志
|
|||
|
logger = logging.getLogger(__name__)
|
|||
|
|
|||
|
@router.post("/zuowen_chinese")
|
|||
|
async def zuowen_chinese(request: Request):
|
|||
|
"""
|
|||
|
批阅中文作文接口
|
|||
|
参数:
|
|||
|
grade_name: 年级名称 (如: 三年级, 四年级)
|
|||
|
image_url: 已上传的作文图片URL
|
|||
|
返回:
|
|||
|
SSE流式响应,包含作文识别和批阅结果
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 获取请求参数
|
|||
|
try:
|
|||
|
data = await request.json()
|
|||
|
grade_name = data.get("grade_name")
|
|||
|
image_url = data.get("image_url")
|
|||
|
except Exception:
|
|||
|
# 如果JSON解析失败,尝试从表单数据中获取
|
|||
|
form = await request.form()
|
|||
|
grade_name = form.get("grade_name")
|
|||
|
image_url = form.get("image_url")
|
|||
|
|
|||
|
# 验证参数
|
|||
|
if not grade_name or not image_url:
|
|||
|
raise HTTPException(status_code=400, detail="缺少必要参数: grade_name 或 image_url")
|
|||
|
|
|||
|
async def generate_zuowen_response():
|
|||
|
try:
|
|||
|
# 步骤1: OCR识别作文内容
|
|||
|
yield f"{json.dumps({'step': 'ocr', 'content': '开始识别作文内容...'}, ensure_ascii=False)}"
|
|||
|
content = OcrUtil.recognize_handwriting(image_url)
|
|||
|
yield f"{json.dumps({'step': 'ocr', 'content': content}, ensure_ascii=False)}"
|
|||
|
|
|||
|
# 步骤2: 生成点评提示词
|
|||
|
query_text = f'我将提供一篇{grade_name}学生作文,请点评学生作文,并打分,满分30分。下面是作文内容:{content}'
|
|||
|
yield f"{json.dumps({'step': 'review', 'content': '开始生成作文点评...'}, ensure_ascii=False)}"
|
|||
|
|
|||
|
# 步骤3: 异步调用LLM进行点评
|
|||
|
full_response = []
|
|||
|
async for chunk in LlmUtil.get_llm_response_async(query_text):
|
|||
|
if chunk:
|
|||
|
full_response.append(chunk)
|
|||
|
yield f"{json.dumps({'step': 'review', 'content': chunk}, ensure_ascii=False)}"
|
|||
|
|
|||
|
# 发送完成信号
|
|||
|
yield f"{json.dumps({'step': 'review', 'content': '作文点评完成', 'done': True}, ensure_ascii=False)}"
|
|||
|
yield f"DONE"
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"作文批阅异常: {str(e)}")
|
|||
|
yield f"{json.dumps({'error': f'处理异常: {str(e)}'}, ensure_ascii=False)}"
|
|||
|
yield f"DONE"
|
|||
|
|
|||
|
# 返回SSE响应
|
|||
|
return EventSourceResponse(generate_zuowen_response())
|
|||
|
except HTTPException as e:
|
|||
|
raise e
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"接口异常: {str(e)}")
|
|||
|
raise HTTPException(status_code=500, detail=f"服务器内部错误: {str(e)}")
|
|||
|
|
|||
|
|
|||
|
@router.post("/zuowen_english")
|
|||
|
async def zuowen_english(request: Request):
|
|||
|
"""
|
|||
|
批阅英文作文接口
|
|||
|
参数:
|
|||
|
grade_name: 年级名称 (如: Grade 3, Grade 4)
|
|||
|
image_url: 已上传的作文图片URL
|
|||
|
返回:
|
|||
|
SSE流式响应,包含作文识别和批阅结果
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 获取请求参数
|
|||
|
try:
|
|||
|
data = await request.json()
|
|||
|
grade_name = data.get("grade_name")
|
|||
|
image_url = data.get("image_url")
|
|||
|
except Exception:
|
|||
|
# 如果JSON解析失败,尝试从表单数据中获取
|
|||
|
form = await request.form()
|
|||
|
grade_name = form.get("grade_name")
|
|||
|
image_url = form.get("image_url")
|
|||
|
|
|||
|
# 验证参数
|
|||
|
if not grade_name or not image_url:
|
|||
|
raise HTTPException(status_code=400, detail="缺少必要参数: grade_name 或 image_url")
|
|||
|
|
|||
|
async def generate_zuowen_response():
|
|||
|
try:
|
|||
|
# 步骤1: OCR识别作文内容
|
|||
|
yield f"{json.dumps({'step': 'ocr', 'content': '开始识别英文作文内容...'}, ensure_ascii=False)}"
|
|||
|
content = OcrUtil.recognize_handwriting(image_url)
|
|||
|
yield f"{json.dumps({'step': 'ocr', 'content': content}, ensure_ascii=False)}"
|
|||
|
|
|||
|
# 步骤2: 生成点评提示词
|
|||
|
query_text = f"I will provide an English composition from a {grade_name} student. Please evaluate the composition and give a score out of 30 points. Here is the composition content: {content}"
|
|||
|
yield f"{json.dumps({'step': 'review', 'content': '开始生成作文点评...'}, ensure_ascii=False)}"
|
|||
|
|
|||
|
# 步骤3: 异步调用LLM进行点评
|
|||
|
full_response = []
|
|||
|
async for chunk in LlmUtil.get_llm_response_async(query_text):
|
|||
|
if chunk:
|
|||
|
full_response.append(chunk)
|
|||
|
yield f"{json.dumps({'step': 'review', 'content': chunk}, ensure_ascii=False)}"
|
|||
|
|
|||
|
# 发送完成信号
|
|||
|
yield f"{json.dumps({'step': 'review', 'content': '作文点评完成', 'done': True}, ensure_ascii=False)}"
|
|||
|
yield f"DONE"
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"英文作文批阅异常: {str(e)}")
|
|||
|
yield f"{json.dumps({'error': f'处理异常: {str(e)}'}, ensure_ascii=False)}"
|
|||
|
yield f"DONE"
|
|||
|
|
|||
|
# 返回SSE响应
|
|||
|
return EventSourceResponse(generate_zuowen_response())
|
|||
|
except HTTPException as e:
|
|||
|
raise e
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"英文作文批阅接口异常: {str(e)}")
|
|||
|
raise HTTPException(status_code=500, detail=f"服务器内部错误: {str(e)}")
|