"""Shared yaml config path resolver — works for monorepo dev + flat container deploy. 【這個模組在做什麼?】 ssrf_policy.py + pricing_config.py 都需要讀 monorepo 共用的 yaml (packages/ssrf-policy/policy.yaml、packages/pricing-config/*.yaml)。 本機 dev 跑 Python 時 __file__ 在 apps/ml-service/src/,往上四層 (parents[3]) 正好是 monorepo root。但 HF Spaces / Render 部署是把 apps/ml-service/* 攤平 當 repo root,container 內 __file__ = /app/src/X.py,parents[3] 直接 IndexError。 【這個 helper 的作法】 - 先試 monorepo layout(parents[3] / packages/ 存在) - fallback 到 container 的 _shared/ 目錄(GitHub Actions sync 時 stage 進去 的 yaml 副本,Dockerfile 把它 COPY 到 /app/_shared/) - 都找不到就 raise,避免 silent fallback 讓 SSRF policy 變空白規則 【為什麼不用 env var override】 ssrf_policy.py 已有 SSRF_POLICY_PATH,但要 caller 自己拼路徑——HF Variables / Render env 容易設錯。這個 helper 走「資料隨 image 走」原則,少一個運維變數。 """ from __future__ import annotations from pathlib import Path def shared_packages_dir() -> Path: """Return the directory that holds shared monorepo `packages/*` yaml configs. Resolution order: 1. **Monorepo layout**: `/packages/` exists relative to this file (used for local `pnpm dev` / `uv run` workflows). 2. **Flat container layout**: `/_shared/` exists (sync-hf-space.yml stages monorepo packages here before upload; Dockerfile COPYs it into /app/_shared/). Raises: FileNotFoundError if neither layout has the expected directory. """ here = Path(__file__).resolve() # Monorepo: apps/ml-service/src/_shared_paths.py → parents[3] = repo root # Path.resolve().parents is finite; container path /app/src/X.py only has 3 # parents (/app/src, /app, /), so probe length first to avoid IndexError. if len(here.parents) > 3: candidate = here.parents[3] / "packages" if candidate.is_dir(): return candidate # Container: /app/src/_shared_paths.py → parents[1] = /app container_candidate = here.parents[1] / "_shared" if container_candidate.is_dir(): return container_candidate raise FileNotFoundError( "Cannot locate shared packages directory. Tried monorepo " f"({here.parents[3] / 'packages' if len(here.parents) > 3 else ''}) " f"and container fallback ({container_candidate}). " "Ensure either the monorepo layout is intact or the deployment image " "has _shared/ populated by the sync workflow." )