This commit is contained in:
parent
b08f29e8bf
commit
5faf98c651
96
AGENTS.md
96
AGENTS.md
|
|
@ -824,6 +824,8 @@ mark_x = answer_bbox[2] + 10 # 紧贴答案框
|
||||||
- ✅ 添加 URL 可访问性验证和 HTTP Headers 支持
|
- ✅ 添加 URL 可访问性验证和 HTTP Headers 支持
|
||||||
- ✅ 实现超时保护机制(单任务120秒,总任务按图片数量计算)
|
- ✅ 实现超时保护机制(单任务120秒,总任务按图片数量计算)
|
||||||
- ✅ 删除未使用的 S3 存储模块(src/storage/s3/)
|
- ✅ 删除未使用的 S3 存储模块(src/storage/s3/)
|
||||||
|
- ✅ 修复 LLM 认证错误,使用 new_context() 初始化 Context
|
||||||
|
- ✅ 修复 Docker 环境空请求体错误,添加请求体验证
|
||||||
- 提供真实的多页作业图片进行完整流程测试
|
- 提供真实的多页作业图片进行完整流程测试
|
||||||
- 优化HTML报告的图片展示布局
|
- 优化HTML报告的图片展示布局
|
||||||
- 支持PDF格式答案文档
|
- 支持PDF格式答案文档
|
||||||
|
|
@ -965,3 +967,97 @@ for future in concurrent.futures.as_completed(
|
||||||
- ✅ 兼容阿里云 CDN 等需要特殊 Headers 的服务
|
- ✅ 兼容阿里云 CDN 等需要特殊 Headers 的服务
|
||||||
- ✅ 超时任务自动跳过,避免长时间阻塞
|
- ✅ 超时任务自动跳过,避免长时间阻塞
|
||||||
- ✅ 线上部署环境测试通过
|
- ✅ 线上部署环境测试通过
|
||||||
|
## Docker 环境请求验证修复(2026-03-30)
|
||||||
|
### 问题诊断
|
||||||
|
在 Docker 环境中运行时报错:
|
||||||
|
```
|
||||||
|
Invalid JSON format: Expecting value: line 1 column 1 (char 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因分析**:
|
||||||
|
1. 请求体为空(例如,使用 GET 请求而不是 POST)
|
||||||
|
2. Content-Type 不是 application/json
|
||||||
|
3. 负载均衡器或代理可能过滤了请求体
|
||||||
|
|
||||||
|
### 解决方案
|
||||||
|
在 `http_run` 和 `http_stream_run` 函数中添加请求体验证:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 检查请求体是否为空
|
||||||
|
if not body_text or body_text.strip() == "":
|
||||||
|
logger.error(f"Empty request body for run_id={ctx.run_id}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Request body is empty. Please provide a valid JSON payload."
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检查 Content-Type
|
||||||
|
content_type = request.headers.get("content-type", "")
|
||||||
|
if "application/json" not in content_type.lower():
|
||||||
|
logger.error(f"Invalid Content-Type: {content_type}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Content-Type must be 'application/json', got: {content_type}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
- `src/main.py`
|
||||||
|
- `http_run` 函数:添加请求体验证
|
||||||
|
- `http_stream_run` 函数:添加请求体验证
|
||||||
|
|
||||||
|
### 正确的请求格式
|
||||||
|
|
||||||
|
**使用 curl**:
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/stream_run \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"student_homework": [
|
||||||
|
{
|
||||||
|
"student_id": 1,
|
||||||
|
"student_name": "张三",
|
||||||
|
"homework_images": ["https://example.com/image.jpg"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"answer_doc_url": "",
|
||||||
|
"comment_max_length": 50,
|
||||||
|
"max_concurrent": 5
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用 Python requests**:
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = "http://localhost:8000/stream_run"
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
data = {
|
||||||
|
"student_homework": [
|
||||||
|
{
|
||||||
|
"student_id": 1,
|
||||||
|
"student_name": "张三",
|
||||||
|
"homework_images": ["https://example.com/image.jpg"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"answer_doc_url": "",
|
||||||
|
"comment_max_length": 50,
|
||||||
|
"max_concurrent": 5
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
print(response.json())
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意事项**:
|
||||||
|
- ✅ 必须使用 POST 请求
|
||||||
|
- ✅ Content-Type 必须是 `application/json`
|
||||||
|
- ✅ 请求体必须是非空的 JSON 字符串
|
||||||
|
- ❌ 不要使用 GET 请求
|
||||||
|
- ❌ 不要发送空请求体
|
||||||
|
- ❌ 不要省略 Content-Type 头
|
||||||
|
|
||||||
|
### 效果
|
||||||
|
- ✅ 空请求体返回友好的错误信息
|
||||||
|
- ✅ 错误的 Content-Type 返回明确的错误提示
|
||||||
|
- ✅ 测试通过
|
||||||
|
|
|
||||||
35
src/main.py
35
src/main.py
|
|
@ -251,6 +251,23 @@ async def http_run(request: Request) -> Dict[str, Any]:
|
||||||
body_text = str(raw_body)
|
body_text = str(raw_body)
|
||||||
raise HTTPException(status_code=400,
|
raise HTTPException(status_code=400,
|
||||||
detail=f"Invalid JSON format: {body_text}, traceback: {traceback.format_exc()}, error: {e}")
|
detail=f"Invalid JSON format: {body_text}, traceback: {traceback.format_exc()}, error: {e}")
|
||||||
|
|
||||||
|
# 检查请求体是否为空
|
||||||
|
if not body_text or body_text.strip() == "":
|
||||||
|
logger.error(f"Empty request body in http_run for run_id={ctx.run_id}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Request body is empty. Please provide a valid JSON payload."
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检查 Content-Type
|
||||||
|
content_type = request.headers.get("content-type", "")
|
||||||
|
if "application/json" not in content_type.lower():
|
||||||
|
logger.error(f"Invalid Content-Type in http_run: {content_type}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Content-Type must be 'application/json', got: {content_type}"
|
||||||
|
)
|
||||||
|
|
||||||
ctx = new_context(method="run", headers=request.headers)
|
ctx = new_context(method="run", headers=request.headers)
|
||||||
# 优先使用上游指定的 run_id,保证 cancel 能精确匹配
|
# 优先使用上游指定的 run_id,保证 cancel 能精确匹配
|
||||||
|
|
@ -346,6 +363,24 @@ async def http_stream_run(request: Request):
|
||||||
body_text = str(raw_body)
|
body_text = str(raw_body)
|
||||||
raise HTTPException(status_code=400,
|
raise HTTPException(status_code=400,
|
||||||
detail=f"Invalid JSON format: {body_text}, traceback: {extract_core_stack()}, error: {e}")
|
detail=f"Invalid JSON format: {body_text}, traceback: {extract_core_stack()}, error: {e}")
|
||||||
|
|
||||||
|
# 检查请求体是否为空
|
||||||
|
if not body_text or body_text.strip() == "":
|
||||||
|
logger.error(f"Empty request body in http_stream_run for run_id={ctx.run_id}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Request body is empty. Please provide a valid JSON payload."
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检查 Content-Type
|
||||||
|
content_type = request.headers.get("content-type", "")
|
||||||
|
if "application/json" not in content_type.lower():
|
||||||
|
logger.error(f"Invalid Content-Type in http_stream_run: {content_type}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Content-Type must be 'application/json', got: {content_type}"
|
||||||
|
)
|
||||||
|
|
||||||
run_id = ctx.run_id
|
run_id = ctx.run_id
|
||||||
is_agent = graph_helper.is_agent_proj()
|
is_agent = graph_helper.is_agent_proj()
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue