diff --git a/mcp-server/src/legal_mcp/config.py b/mcp-server/src/legal_mcp/config.py index 7a96859..fd907ac 100644 --- a/mcp-server/src/legal_mcp/config.py +++ b/mcp-server/src/legal_mcp/config.py @@ -54,6 +54,10 @@ REDIS_URL = os.environ.get("REDIS_URL", "redis://127.0.0.1:6380/0") # pinned. HALACHA_EXTRACT_MODEL = os.environ.get("HALACHA_EXTRACT_MODEL", "claude-opus-4-8") HALACHA_EXTRACT_EFFORT = os.environ.get("HALACHA_EXTRACT_EFFORT", "xhigh") +# Digest (X12) metadata extraction is a simpler, high-volume task (concept tag, +# headline, underlying citation, tags from a one-page summary) โ€” Sonnet is the +# speed/cost sweet-spot here, unlike halacha extraction which pins Opus. Tune via env. +DIGEST_EXTRACT_MODEL = os.environ.get("DIGEST_EXTRACT_MODEL", "claude-sonnet-4-6") # Effort for BULK queue-drain extraction (process_pending over many precedents). # xhigh is the quality sweet-spot for a single precedent but very slow at scale # (a 64-chunk case โ‰ˆ 20 min). Bulk drains use a lighter effort to cut wall-clock; diff --git a/mcp-server/src/legal_mcp/services/digest_metadata_extractor.py b/mcp-server/src/legal_mcp/services/digest_metadata_extractor.py index 45ef0fd..6745439 100644 --- a/mcp-server/src/legal_mcp/services/digest_metadata_extractor.py +++ b/mcp-server/src/legal_mcp/services/digest_metadata_extractor.py @@ -19,6 +19,7 @@ from __future__ import annotations import logging from datetime import date as date_type +from legal_mcp import config from legal_mcp.config import parse_llm_json from legal_mcp.services import claude_session @@ -79,13 +80,17 @@ def _norm_date(result: dict, key: str) -> date_type | None: return None -async def extract(raw_text: str) -> dict: +async def extract(raw_text: str, model: str | None = None) -> dict: """Extract digest metadata from raw text. Returns a dict (never raises). Keys: yomon_number, digest_date (date|None), concept_tag, headline_holding, summary, underlying_citation, underlying_court, underlying_date (date|None), underlying_judge, practice_area, appeal_subtype, subject_tags (list[str]). Missing/invalid fields are omitted so the caller's merge keeps user values. + + Model: defaults to ``config.DIGEST_EXTRACT_MODEL`` (Sonnet โ€” this is a + high-volume, simple extraction; no need for Opus). Override per-call via + ``model``. """ text = (raw_text or "").strip() if not text: @@ -95,6 +100,7 @@ async def extract(raw_text: str) -> dict: try: result = await claude_session.query_json( user_msg, system=DIGEST_EXTRACTION_PROMPT, + model=(model or config.DIGEST_EXTRACT_MODEL or None), ) except Exception as e: # surfaced as warning, not swallowed silently (ยง6) logger.warning("digest_metadata_extractor: query failed: %s", e)