refactor(precedents): keep all LLM calls on the local-MCP path
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m28s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m28s
Architectural correction: every claude_session caller in this project
runs through the local MCP server (~/.claude.json points at
/home/chaim/legal-ai/mcp-server/.venv/bin/python). The Coolify container
has no `claude` CLI and no claude.ai session, so any LLM call originating
from web/ FastAPI fails with "Claude CLI not found" — which is exactly
what we hit on 403-17.
The earlier Anthropic SDK fallback would have made it work, but at
direct API cost. The chair's preference is to stay on the claude.ai
session for everything. So:
- claude_session.py: removed the SDK fallback, restored CLI-only.
The error message now points the next person at the architectural
rule in the module docstring instead of papering over it.
- precedent_library.py:ingest_precedent (called from FastAPI on upload)
now does only the non-LLM half: extract → chunk → embed → store.
Sets halacha_extraction_status='pending' for the chair to act on.
- reextract_halachot / reextract_metadata kept, but lazy-import their
extractors so the FastAPI path can't accidentally pull them in. They
are reachable only via the MCP tools precedent_extract_halachot /
precedent_extract_metadata, which run locally with CLI.
- Removed POST /api/precedent-library/{id}/extract-halachot and
/extract-metadata — they were dead ends from the container.
- Dropped the `anthropic` Python dep that the SDK fallback required.
- UI: removed the "refresh halachot" and "sparkles metadata" buttons
that called those endpoints. Edit sheet now points the chair at the
MCP tool names instead.
Halacha and metadata extraction for an uploaded precedent now happen
when the chair (via Claude Code) runs:
mcp__legal-ai__precedent_extract_metadata <case_law_id>
mcp__legal-ai__precedent_extract_halachot <case_law_id>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,14 +22,13 @@ from typing import Awaitable, Callable
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from legal_mcp import config
|
||||
from legal_mcp.services import (
|
||||
chunker,
|
||||
db,
|
||||
embeddings,
|
||||
extractor,
|
||||
halacha_extractor,
|
||||
precedent_metadata_extractor,
|
||||
)
|
||||
from legal_mcp.services import chunker, db, embeddings, extractor
|
||||
|
||||
# Note: halacha_extractor and precedent_metadata_extractor are NOT imported
|
||||
# at module load. They are imported lazily inside the dedicated re-extract
|
||||
# entry points so that `ingest_precedent` (called from the FastAPI container,
|
||||
# where `claude` CLI is unavailable) cannot accidentally pull them in. See
|
||||
# the architectural rule in services/claude_session.py.
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -189,36 +188,30 @@ async def ingest_precedent(
|
||||
]
|
||||
stored_chunks = await db.store_precedent_chunks(case_law_id, chunk_dicts)
|
||||
|
||||
# Pipeline split: the container does the non-LLM half (extract +
|
||||
# chunk + embed + store). LLM-driven extraction (metadata, halachot)
|
||||
# runs separately via the MCP tools `precedent_extract_metadata` /
|
||||
# `precedent_extract_halachot` from local Claude Code, where
|
||||
# `claude` CLI is available. Mark statuses so the chair can see
|
||||
# what's pending in the UI.
|
||||
await db.set_case_law_extraction_status(case_law_id, "completed")
|
||||
await db.set_case_law_halacha_status(case_law_id, "pending")
|
||||
|
||||
await progress("extracting_metadata", 65, "מחלץ מטא-דאטה (תקציר, תגיות)")
|
||||
try:
|
||||
metadata_result = await precedent_metadata_extractor.extract_and_apply(
|
||||
case_law_id,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("metadata extraction failed (non-fatal): %s", e)
|
||||
metadata_result = {"status": "failed", "fields": []}
|
||||
|
||||
await progress("extracting_halachot", 80, "מחלץ הלכות / יישומים")
|
||||
halacha_result = await halacha_extractor.extract(case_law_id)
|
||||
|
||||
msg = (
|
||||
f"הוכנס לספרייה: {stored_chunks} chunks, "
|
||||
f"{halacha_result.get('stored', 0)} פריטים ממתינים לאישור"
|
||||
await progress(
|
||||
"completed",
|
||||
100,
|
||||
f"הוכנס לספרייה: {stored_chunks} chunks. "
|
||||
f"חילוץ הלכות ומטא-דאטה — להפעיל מ-Claude Code "
|
||||
f"(precedent_extract_halachot / precedent_extract_metadata).",
|
||||
)
|
||||
if metadata_result.get("fields"):
|
||||
msg += f"; מולאו אוטומטית: {', '.join(metadata_result['fields'])}"
|
||||
await progress("completed", 100, msg)
|
||||
|
||||
return {
|
||||
"status": "completed",
|
||||
"case_law_id": str(case_law_id),
|
||||
"chunks": stored_chunks,
|
||||
"halachot": halacha_result.get("stored", 0),
|
||||
"halachot_extracted_raw": halacha_result.get("extracted", 0),
|
||||
"halachot_verified": halacha_result.get("verified", 0),
|
||||
"metadata_filled": metadata_result.get("fields", []),
|
||||
"halachot": 0,
|
||||
"halachot_pending": True,
|
||||
"metadata_filled": [],
|
||||
"pages": page_count,
|
||||
}
|
||||
|
||||
@@ -233,7 +226,15 @@ async def reextract_halachot(
|
||||
case_law_id: UUID | str,
|
||||
progress: ProgressCb | None = None,
|
||||
) -> dict:
|
||||
"""Re-run the halacha extractor on an existing precedent. Idempotent."""
|
||||
"""Re-run the halacha extractor on an existing precedent. Idempotent.
|
||||
|
||||
**MCP-tool-only path.** This function calls into ``halacha_extractor``,
|
||||
which calls ``claude_session`` — the local CLI is required. Invoking
|
||||
this from the FastAPI container will raise ``Claude CLI not found``.
|
||||
See the architectural rule in ``services/claude_session.py``.
|
||||
"""
|
||||
from legal_mcp.services import halacha_extractor
|
||||
|
||||
progress = progress or _noop_progress
|
||||
if isinstance(case_law_id, str):
|
||||
case_law_id = UUID(case_law_id)
|
||||
@@ -261,7 +262,11 @@ async def reextract_metadata(
|
||||
Only fills empty fields (subject_tags, summary, headnote, key_quote,
|
||||
appeal_subtype, and case_name when it equals the citation). User
|
||||
values are preserved.
|
||||
|
||||
**MCP-tool-only path** — same constraint as :func:`reextract_halachot`.
|
||||
"""
|
||||
from legal_mcp.services import precedent_metadata_extractor
|
||||
|
||||
progress = progress or _noop_progress
|
||||
if isinstance(case_law_id, str):
|
||||
case_law_id = UUID(case_law_id)
|
||||
|
||||
Reference in New Issue
Block a user