Files
jerryandClaude Fable 5 2f82c4a8dc P4/M5: agent/legacy-rag 源码迁移(449 模块 191K 行)+ 影子 7911/7912 冒烟
- 按生产 7900 容器黄金清单迁移(compile.manifest 449 条目,0 缺失)
- 入口双态:api.app(生产同源 3 路由)/ api.main(裁剪 11 路由族 91 路径)
- 修复:advanced/__init__ 空文件(包标记误覆盖,已从主项目恢复)
- 修复:api/rag_routes.py 跨环境 site-packages 注入改 LEGACY_GF_SITE_PACKAGES 门控
- 修复:scripts/utils 缺失(动态加载 reasoning_engine)
- 影子验证:kg/stats 数据连通(6.2s,与 7900 同源 normalized 后端)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:20:41 +08:00

197 lines
5.4 KiB
Python
Executable File
Raw Permalink 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.
#!/usr/bin/env python3
"""
方法学提取 API - FastAPI 版本
提供接口:
- POST /extract - 从 PDF 提取方法学参数
- GET /result/{job_id} - 获取提取结果
"""
import os
import sys
import json
import uuid
from pathlib import Path
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Dict, List, Optional
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.rag.extractors.methodology.simple_extractor import SimpleExtractor
app = FastAPI(title="Methodology Extraction API")
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 存储提取结果
results = {}
class ExtractionResult(BaseModel):
job_id: str
status: str
data: Optional[Dict] = None
error: Optional[str] = None
@app.post("/extract")
async def extract_methodology(pdf: UploadFile = File(...)):
"""从 PDF 提取方法学参数"""
# 生成 job_id
job_id = str(uuid.uuid4())[:8]
# 保存 PDF
upload_dir = Path(__file__).parent.parent / 'uploads'
upload_dir.mkdir(exist_ok=True)
pdf_path = upload_dir / f"{job_id}_{pdf.filename}"
with open(pdf_path, "wb") as f:
content = await pdf.read()
f.write(content)
# 提取
try:
extractor = SimpleExtractor()
result_text = extractor.extract(pdf_path)
# 解析为结构化数据
structured = parse_extraction_result(result_text)
# 保存结果
results[job_id] = {
'status': 'completed',
'pdf_path': str(pdf_path),
'raw_text': result_text,
'data': structured
}
return {
'job_id': job_id,
'status': 'completed',
'data': structured
}
except Exception as e:
results[job_id] = {
'status': 'failed',
'error': str(e)
}
raise HTTPException(status_code=500, detail=str(e))
@app.get("/result/{job_id}")
async def get_result(job_id: str):
"""获取提取结果"""
if job_id not in results:
raise HTTPException(status_code=404, detail="Job not found")
return results[job_id]
def parse_extraction_result(text: str) -> Dict:
"""解析提取结果为结构化数据"""
data = {
'species': {
'target': '',
'references': []
},
'gene_family': {
'name': '',
'pfam_id': ''
},
'stages': []
}
# 简单解析
lines = text.split('\n')
for line in lines:
line = line.strip()
# 物种
if '目标物种' in line or 'target' in line.lower():
if ':' in line:
data['species']['target'] = line.split(':', 1)[-1].strip()
elif '参考物种' in line or 'references' in line.lower():
if ':' in line:
refs = line.split(':', 1)[-1].strip()
data['species']['references'] = [r.strip() for r in refs.split('、') if r.strip()]
elif '基因家族' in line:
if ':' in line:
parts = line.split(':', 1)[-1].strip()
if '(' in parts and 'Pfam:' in parts:
name = parts.split('(')[0].strip()
pfam = parts.split('Pfam:')[-1].split(')')[0].strip()
data['gene_family']['name'] = name
data['gene_family']['pfam_id'] = pfam
# 默认阶段
data['stages'] = [
{
'id': 'identify',
'name': '1. 基因家族鉴定',
'params': [
{'name': 'tool', 'label': '工具', 'value': 'BLASTp + HMMER'},
{'name': 'pfam_id', 'label': 'Pfam ID', 'value': 'PF03110'},
{'name': 'e_value', 'label': 'E-value', 'value': ''},
{'name': 'identity', 'label': 'Identity', 'value': ''}
]
},
{
'id': 'phylogeny',
'name': '2. 系统发育分析',
'params': [
{'name': 'software', 'label': '软件', 'value': 'MEGA 6.0'},
{'name': 'method', 'label': '方法', 'value': 'Neighbor-Joining'},
{'name': 'bootstrap', 'label': 'Bootstrap', 'value': '1000'}
]
},
{
'id': 'motif',
'name': '3. 保守基序分析',
'params': [
{'name': 'tool', 'label': '工具', 'value': 'MEME'},
{'name': 'max_motifs', 'label': 'Max motifs', 'value': '10'}
]
},
{
'id': 'structure',
'name': '4. 基因结构分析',
'params': [
{'name': 'tool', 'label': '工具', 'value': 'GSDS'}
]
},
{
'id': 'synteny',
'name': '5. 共线性分析',
'params': [
{'name': 'tool', 'label': '工具', 'value': 'MCScanX'}
]
},
{
'id': 'expression',
'name': '6. 表达分析',
'params': [
{'name': 'method', 'label': '方法', 'value': 'qRT-PCR'}
]
}
]
return data
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5001)