""" 报价模板访问壳。 真相在主账:后台上传/启用/停用 → 对象存储存字节 → 主账存元数据。 智能体生成报价前经 ledger 拉取已启用模板指针,再从对象存储取字节。 本文件职责:声明「从主账列模板」的空 API;可选本地缓存目录仅联调。 禁止:把仓库 resources 当生产模板真相;禁止在本包发明价格或改 quoteVersion。 """ from __future__ import annotations import logging from dataclasses import dataclass from pathlib import Path from typing import Any, Optional from agent.ledger import LedgerClient, LedgerResponse logger = logging.getLogger(__name__) @dataclass(frozen=True) class TemplateEntry: """一条模板元数据(来自主账或联调缓存)。""" code: str filename: str = "" object_key: str = "" enabled: bool = False source: str = "ledger" def local_cache_dir() -> Path: """ 可选本机缓存目录(非生产真相)。 正式模板由后台上传;此目录仅开发落盘缓存,可空。 """ here = Path(__file__).resolve() return here.parents[2] / "resources" / "quote_templates" def list_templates_from_ledger( client: Optional[LedgerClient] = None, ) -> LedgerResponse: """ 向主账查询已启用报价模板列表(壳)。 正式路径以主账 OpenAPI 为准;当前返回 not_implemented,不假装本地文件即生产模板。 """ c = client or LedgerClient.from_settings() logger.info("quote_templates.list_from_ledger(壳,未打真实 API)") return LedgerResponse( ok=False, status_code=501, error="not_implemented:list_quote_templates", data={"hint": "admin_upload_via_ledger"}, ) def list_local_cache_files() -> list[TemplateEntry]: """ 列出本机缓存目录中的 xlsx(仅联调;可空)。 不等于后台已启用模板。 """ root = local_cache_dir() if not root.is_dir(): return [] out: list[TemplateEntry] = [] for path in sorted(root.iterdir()): if not path.is_file(): continue if path.suffix.lower() not in {".xlsx", ".xlsm", ".xltx"}: continue if path.name.startswith("."): continue out.append( TemplateEntry( code=path.stem, filename=path.name, object_key="", enabled=False, source="local_cache", ) ) logger.info("quote_templates.local_cache count=%s", len(out)) return out def list_template_registry() -> list[TemplateEntry]: """ 兼容旧名:框架阶段返回本地缓存列表(可空)。 生产应改用 list_templates_from_ledger + 对象存储下载。 """ return list_local_cache_files() def registry_as_dicts() -> list[dict[str, Any]]: """便于 JSON/自检。""" return [ { "code": e.code, "filename": e.filename, "object_key": e.object_key, "enabled": e.enabled, "source": e.source, } for e in list_template_registry() ]