""" 千问:整理 Excel/PDF 抽出的原文。 本文件职责:把抽文本结果交给 qwen-plus,只整理看得见的字,不填询价字段。 属于 llm 包。单测注入 chat_fn。禁止算最终缺项、禁止发明港口/箱型。 """ from __future__ import annotations import logging from typing import Any, Optional from agent.llm.modes import LlmMode logger = logging.getLogger(__name__) MODE = LlmMode.ATTACHMENT_TEXT ATTACH_SYSTEM = ( "下面是从 Excel 或 PDF 抽出来的原文。请整理成一段可读的询价口播:" "只保留材料里已经出现的文字和数字,原样保留港口、机场三字码、箱型、重量。" "看不清或没出现的不要猜,不要补询价缺项,不要发明港口、重量或日期。" "材料完全没有询价信息时输出空。" ) def mode_meta() -> dict[str, Any]: return { "mode": MODE.value, "provider": "qwen", "purpose": "attachment_text", "implemented": True, } def _pack_text(text: str, *, stub: bool = False) -> dict[str, Any]: body = (text or "").strip() return { "ok": True, "stub": stub, "mode": MODE.value, "text": body, "empty": not bool(body), "error": "", } def invoke_shell(*, payload: dict[str, Any]) -> dict[str, Any]: """ 无网/单测入口:优先 assistant_text;否则把 raw_text 当整理结果。 """ data = dict(payload or {}) if "assistant_text" in data: return _pack_text(str(data.get("assistant_text") or ""), stub=True) if "raw_text" in data: return _pack_text(str(data.get("raw_text") or ""), stub=True) return { "ok": False, "stub": True, "mode": MODE.value, "text": "", "empty": True, "error": "attachment_text_shell_not_implemented", "echo_keys": sorted(data.keys()), } def invoke_attachment_text( *, payload: dict[str, Any], chat_fn: Optional[Any] = None, allow_network: bool = False, ) -> dict[str, Any]: """ 整理一份抽出原文。chat_fn 单测注入;无网走 invoke_shell。 """ data = dict(payload or {}) raw = str(data.get("raw_text") or "") if chat_fn is None and not allow_network: return invoke_shell(payload=data) if not raw.strip(): return _pack_text("", stub=chat_fn is None) if chat_fn is None: from agent.llm.http_client import LlmHttpClient client = LlmHttpClient.from_settings() chat_fn = client.chat messages = [ {"role": "system", "content": ATTACH_SYSTEM}, {"role": "user", "content": raw.strip()}, ] result = chat_fn(mode=MODE, messages=messages) if not getattr(result, "ok", False): err = str(getattr(result, "error", "") or "attachment_text_failed") logger.info("attachment_text 失败 err=%s", err) return { "ok": False, "stub": False, "mode": MODE.value, "text": "", "empty": True, "error": err, } packed = _pack_text(str(getattr(result, "content", "") or "")) packed["stub"] = False return packed