File size: 4,538 Bytes
fbd9d3d e5e756a fbd9d3d cc826a1 fbd9d3d e5e756a fbd9d3d e5e756a fbd9d3d e5e756a cc826a1 e5e756a fbd9d3d e5e756a fbd9d3d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | import logging
from typing import Any
from fastapi import APIRouter, HTTPException, Request, Query
from app.plugins.run_log import get_run_log_service, RunStatus
logger = logging.getLogger(__name__)
router = APIRouter()
plugin = None
core = None
def set_plugin_instance(plugin_instance):
"""设置插件实例"""
global plugin
plugin = plugin_instance
def set_core_instance(core_instance):
"""设置核心逻辑实例"""
global core
core = core_instance
@router.get("/status")
async def get_status():
"""获取插件状态"""
if plugin is None:
return {
"name": "content",
"enabled": False,
"message": "插件未加载",
}
return plugin.get_status()
@router.post("/extract")
async def extract_content(request: Request, wait: bool = Query(False, description="是否同步等待提取完成")):
"""
提取内容
默认异步返回 run_id,设置 wait=true 同步等待结果。
"""
if plugin is None or not plugin.enabled:
raise HTTPException(status_code=400, detail="插件未启用")
if core is None:
raise HTTPException(status_code=500, detail="核心逻辑未初始化")
payload = await _parse_extract_payload(request)
url = str(payload.get("url") or "")
include_ocr = _parse_bool(payload.get("include_ocr", True))
if not url or not url.strip():
raise HTTPException(status_code=400, detail="链接不能为空")
# 创建 run
run_service = get_run_log_service()
run = run_service.create_run("content")
# 写入上传事件
run_service.add_event(
run_id=run.run_id,
stage="upload",
message=f"开始提取: {url}",
detail=f"include_ocr={include_ocr}",
)
try:
# 执行提取
result = await core.extract_with_run(
url=url.strip(),
run_id=run.run_id,
include_ocr=include_ocr,
)
if result["success"]:
# 完成 run
run_service.finish_run(
run_id=run.run_id,
status=RunStatus.SUCCEEDED,
result={
"title": result["title"],
"content": result["content"],
"raw_html": result["raw_html"],
"raw_text": result["raw_text"],
"normalized_content": result["normalized_content"],
"images": result["images"],
"images_text": result["images_text"],
"source_type": result["source_type"],
},
)
else:
# 完成 run(失败)
run_service.finish_run(
run_id=run.run_id,
status=RunStatus.FAILED,
error=result["error"],
)
# 如果同步等待,直接返回结果
if wait:
return {
"run_id": run.run_id,
"status": "succeeded" if result["success"] else "failed",
**result,
}
# 异步返回 run_id
return {
"run_id": run.run_id,
"status": "running",
"source_type": result["source_type"],
}
except Exception as e:
# 写入异常事件
run_service.add_event(
run_id=run.run_id,
stage="extract",
message=f"提取异常: {str(e)}",
level="error",
)
run_service.finish_run(
run_id=run.run_id,
status=RunStatus.FAILED,
error=str(e),
)
raise HTTPException(status_code=500, detail=f"提取失败: {str(e)}")
async def _parse_extract_payload(request: Request) -> dict[str, Any]:
"""解析提取请求,兼容JSON和表单提交"""
content_type = request.headers.get("content-type", "").lower()
if "application/json" in content_type:
try:
data = await request.json()
except Exception as e:
logger.error(f"解析内容提取JSON请求失败: {e}")
raise HTTPException(status_code=400, detail="请求体不是有效JSON") from e
return data if isinstance(data, dict) else {}
form = await request.form()
return dict(form)
def _parse_bool(value: Any) -> bool:
"""解析布尔参数"""
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() in {"1", "true", "yes", "on"}
return bool(value)
|