更新 app.py
图片切片改进
This commit is contained in:
parent
715829d763
commit
fd8b4b6614
151
app.py
151
app.py
@ -40,7 +40,7 @@ from pydantic import BaseModel
|
||||
from dataprocess import _split_markdown
|
||||
import uuid
|
||||
import json
|
||||
from fileparse_util import process_document
|
||||
from fileparse_util import process_document, extract_image_caption
|
||||
# import hanlp
|
||||
from itertools import groupby
|
||||
import requests
|
||||
@ -92,7 +92,7 @@ from default_ontology_config import (
|
||||
get_relationships,
|
||||
get_relationship_types,
|
||||
)
|
||||
from config import NEO4J_CONFIG,URL,API_URLS,API_OTHER_URLS,VLM_CONCURRENCY
|
||||
from config import NEO4J_CONFIG,URL,API_URLS,API_OTHER_URLS,VLM_CONCURRENCY,SEARCH_DIR
|
||||
converter = Doc2PDF()
|
||||
DATA_DIR = "/app/files"
|
||||
# ================== 全局资源容器 ==================
|
||||
@ -102,7 +102,6 @@ _resources: Dict[str, Any] = {
|
||||
"async_driver": None,
|
||||
"embedder": None,
|
||||
}
|
||||
|
||||
# 图谱检索和索引生成
|
||||
try:
|
||||
from graph_search.graph_service import GraphService
|
||||
@ -1740,7 +1739,7 @@ async def kg_build(request: KGRequest):
|
||||
except Exception as e:
|
||||
logger.warning(f"保存 kg_build 配置缓存失败: {e}")
|
||||
|
||||
# 后续进程启动逻辑保持不变 ...
|
||||
# 后续进程启动逻辑保持不变 ...https://gitea.zkzdht.com/Ascend/htknow
|
||||
cancel_flag_path = f"/tmp/kg_cancel_{task_id}"
|
||||
if os.path.exists(cancel_flag_path):
|
||||
os.remove(cancel_flag_path)
|
||||
@ -2036,7 +2035,10 @@ class Base64ImageRequest(BaseModel):
|
||||
)
|
||||
filename: str = Field(..., description="Original image filename, for example test.png")
|
||||
image_base64: str = Field(..., description="Base64 image content, with or without data:image/... prefix")
|
||||
|
||||
original_filename:Optional[str] = Field(
|
||||
None,
|
||||
description="图片 所在pdf文件",
|
||||
)
|
||||
|
||||
async def analyze_image_with_vlm(image_bytes: bytes, suffix: str) -> str:
|
||||
image_url = image_base64_to_data_url(image_bytes, suffix)
|
||||
@ -2046,7 +2048,11 @@ async def analyze_image_with_vlm(image_bytes: bytes, suffix: str) -> str:
|
||||
|
||||
@app.post("/split_image")
|
||||
async def image_base64(request_data: Base64ImageRequest):
|
||||
logger.info(f"Received image: {request_data.filename}")
|
||||
logger.info(f"内容: {request_data.content}")
|
||||
logger.info(f"pdf文件名: {request_data.original_filename}")
|
||||
filename = safe_original_filename(request_data.filename)
|
||||
logger.info(f"filename: {filename}")
|
||||
suffix = PathLib(filename).suffix.lower()
|
||||
if suffix not in {".jpg", ".jpeg", ".png", ".bmp", ".webp", ".gif", ".tif", ".tiff"}:
|
||||
raise HTTPException(status_code=400, detail="Unsupported image type")
|
||||
@ -2056,6 +2062,8 @@ async def image_base64(request_data: Base64ImageRequest):
|
||||
raise HTTPException(status_code=400, detail="decoded image is empty")
|
||||
|
||||
temp_path = None
|
||||
image_caption = []
|
||||
vlm_text = ""
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=processed_suffix) as tmp:
|
||||
tmp.write(image_bytes)
|
||||
@ -2068,28 +2076,47 @@ async def image_base64(request_data: Base64ImageRequest):
|
||||
logger.warning(f"Failed to delete temp image {temp_path}: {e}")
|
||||
finally:
|
||||
temp_path = None
|
||||
if request_data.original_filename:
|
||||
image_caption = await extract_image_caption(
|
||||
SEARCH_DIR,
|
||||
request_data.original_filename,
|
||||
request_data.filename,
|
||||
)
|
||||
|
||||
caption_text = "\n".join(image_caption).strip()
|
||||
|
||||
# image_caption 不为空时使用 caption,
|
||||
# 否则使用原始图片文件名
|
||||
if caption_text:
|
||||
caption_text =caption_text+".jpg"
|
||||
output_filename = caption_text or filename
|
||||
logger.info(f"image_caption: {caption_text}")
|
||||
content = (request_data.content or "").strip()
|
||||
ocr_full_text = (ocr_data.get("full_text") or "").strip()
|
||||
ocr_full_text = ((ocr_data or {}).get("full_text") or "").strip()
|
||||
|
||||
# content 和 OCR 都为空时调用 VLM
|
||||
if not content and not ocr_full_text:
|
||||
vlm_text = await analyze_image_with_vlm(image_bytes, processed_suffix)
|
||||
image_content = "图片文本描述为:" + (vlm_text or "").strip()
|
||||
elif content and ocr_full_text:
|
||||
image_content = f"图片上下文内容为:{content}, {ocr_full_text}"
|
||||
elif content:
|
||||
image_content = "图片上下文内容为:" + content
|
||||
else:
|
||||
image_content = ocr_full_text
|
||||
vlm_text = await analyze_image_with_vlm(
|
||||
image_bytes,
|
||||
processed_suffix,
|
||||
)
|
||||
|
||||
image_content = "图片文件名为:" + filename + ",图片相关内容为:" + image_content
|
||||
image_related_content = ocr_full_text+ "\n" +content+ "\n"+vlm_text
|
||||
|
||||
# image_content = (
|
||||
# f"{output_filename}\n"
|
||||
# f"{image_related_content}"
|
||||
# )
|
||||
logger.info(f"output_filename: {output_filename}")
|
||||
logger.info(f"{image_related_content}")
|
||||
return JSONResponse(
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"image_content": image_content,
|
||||
"image_content": image_related_content,
|
||||
"filename": filename,
|
||||
"jpg_filename": output_filename
|
||||
},
|
||||
}
|
||||
)
|
||||
@ -2205,7 +2232,7 @@ async def split_result(
|
||||
slices = []
|
||||
for ins in contents:
|
||||
slices.append({"content": ins, "positions": []})
|
||||
result_data = {"slices": slices, "images": {}}
|
||||
result_data = {"slices": slices, "images": {},"summary":''}
|
||||
elif suffix in [".xlsx"]:
|
||||
# converter.convert(str(temp_input_path), output_dir=str(DATA_DIR))
|
||||
# temp_base_name = temp_input_path.stem # 如 tmp2bb0l4cn
|
||||
@ -2293,96 +2320,6 @@ class GraphSearchRequest(BaseModel):
|
||||
entry_nodes: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
@app.post("/search_graph——1")
|
||||
async def search_graph(request: GraphSearchRequest, background_tasks: BackgroundTasks):
|
||||
"""
|
||||
========== ========== ========== ========== ========== ==========
|
||||
纯图谱检索
|
||||
|
||||
请求参数:
|
||||
- query: 用户查询文本(必填)
|
||||
- top_k: 返回用于展示的路径数量(默认10)
|
||||
- graph_timeout: 图谱检索超时时间,秒(默认2.0,暂未使用)
|
||||
- entry_nodes: 入口节点信息(可选,不提供则从查询中提取)
|
||||
|
||||
========== ========== ========== ========== ========== ==========
|
||||
"""
|
||||
|
||||
start_time = time.time()
|
||||
query = request.query
|
||||
|
||||
# ========== 参数验证 ==========
|
||||
if not query or not query.strip():
|
||||
raise HTTPException(status_code=400, detail="查询文本不能为空")
|
||||
|
||||
query = query.strip()
|
||||
|
||||
logger.info(f"[search_graph] 图谱检索请求: query='{query}', top_k={request.top_k}")
|
||||
|
||||
try:
|
||||
# ========== 检查依赖是否可用 ==========
|
||||
if not GRAPH_AVAILABLE:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="图谱检索功能依赖未安装,请安装: pip install neo4j langchain langchain-community neo4j-graphrag jieba",
|
||||
)
|
||||
|
||||
# ========== 初始化图谱服务(复用app.py的neo4j driver) ==========
|
||||
# 注意:传入app.py中创建的driver,实现连接复用
|
||||
graph_service = GraphService(driver=driver)
|
||||
|
||||
# ========== 执行图谱检索(简单版本:只判断统计/非统计) ==========
|
||||
# 说明:
|
||||
# - 直接调用 GraphService.search,自动判断统计/非统计查询类型
|
||||
# - query_type="auto" 会自动识别统计类查询(如"多少"、"比例"等)和明细类查询
|
||||
result = await graph_service.search(
|
||||
query=query,
|
||||
entry_nodes=request.entry_nodes,
|
||||
top_k=request.top_k,
|
||||
max_attempts=3,
|
||||
query_type="auto", # 自动判断统计/非统计
|
||||
)
|
||||
|
||||
# ========== 确保返回格式一致 ==========
|
||||
if not isinstance(result, dict):
|
||||
result = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": result if isinstance(result, (list, dict)) else ([] if isinstance(result, list) else {}),
|
||||
"meta": {"elapsed_ms": round((time.time() - start_time) * 1000, 2)},
|
||||
}
|
||||
|
||||
# 确保data存在,可以是列表或字典(新格式:包含 nodes、links、results 的对象)
|
||||
if "data" not in result:
|
||||
result["data"] = {}
|
||||
# 注意:不再强制转换为列表,支持新的对象格式
|
||||
|
||||
# 统计结果数量(兼容新旧格式)
|
||||
if isinstance(result.get("data"), list):
|
||||
result_count = len(result["data"])
|
||||
elif isinstance(result.get("data"), dict):
|
||||
# 新格式:统计 results 数组的长度
|
||||
result_count = len(result["data"].get("results", []))
|
||||
else:
|
||||
result_count = 0
|
||||
|
||||
logger.info(f"[search_graph] 图谱检索完成: 返回 {result_count} 条结果")
|
||||
return result
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
elapsed = time.time() - start_time
|
||||
logger.error(f"[search_graph] 图谱检索失败 (耗时: {elapsed * 1000:.2f}ms): {e}", exc_info=True)
|
||||
return {
|
||||
"code": 500,
|
||||
"message": "图谱检索失败",
|
||||
"data": [],
|
||||
"meta": {"elapsed_ms": round(elapsed * 1000, 2), "error": str(e), "query_type": "error"},
|
||||
}
|
||||
|
||||
|
||||
|
||||
# ========== ========== ========== ========== ========== ==========
|
||||
# Neo4j 索引创建接口
|
||||
# ========== ========== ========== ========== ========== ==========
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user