fix(precedents): מראה-מקום never blank — seed at upload + inline Gemini enrichment (root cause)
Root cause: the upload form "מראה המקום" field is stored only as case_number; citation_formatted was left empty and filled ONLY by the async metadata extraction, which runs in the local drainer (not the container) because it was lumped with halacha extraction. Result: the chair saw an empty מראה-מקום during the window and re-typed it by hand. Fix (hybrid — Option C): - Seed: _create_external_record / create_external_case_law store the chair's typed citation as citation_formatted at INSERT, so the field is never blank. A cited_only→external promotion preserves an existing non-empty value (prior edit). - Enrich: ingest_precedent runs metadata extraction INLINE in-container (gemini_session is direct REST, no local CLI) with force_citation=True, upgrading the seed to the canonical derived citation (parties + reporter + date) before the chair opens the page. Best-effort: on no key / API failure the seed remains and the queued metadata drain stays as the fallback. Halacha extraction is untouched (stays local — claude_session). - apply_to_record / extract_and_apply gain force_citation: re-assemble even when citation_formatted is non-empty, writing only when assembly SUCCEEDS (seed preserved on abstention). The drainer keeps the default (False) so a chair's manual edit in /precedents/[id] is never clobbered. Ops: GOOGLE_GEMINI_API_KEY added to the legal-ai Coolify app (runtime) — the SoT value from Infisical nautilus:/external-apis/gemini, validated against the API. Invariants: G2 (one citation-resolution + one metadata-extraction path, reused — no parallel logic), INV-ID2/X1§3 (citation_formatted stays a derived field; the seed is provisional and upgraded), INV-G10 (chair edits preserved). No schema change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -260,6 +260,7 @@ async def apply_to_record(
|
||||
case_law_id: UUID | str,
|
||||
suggested: dict,
|
||||
overwrite_case_number: bool = False,
|
||||
force_citation: bool = False,
|
||||
) -> dict:
|
||||
"""Merge suggested metadata into the case_law row, filling ONLY empty fields.
|
||||
|
||||
@@ -274,6 +275,13 @@ async def apply_to_record(
|
||||
|
||||
overwrite_case_number: when True, update case_number from case_number_clean
|
||||
even if the field already has a value (used for one-time migration enrichment).
|
||||
|
||||
force_citation: when True, (re)assemble citation_formatted even if the field
|
||||
is non-empty — used by the at-upload inline enrichment to UPGRADE the seeded
|
||||
provisional citation (the raw chair input) to the canonical derived form. The
|
||||
write still happens only when the deterministic assembly SUCCEEDS (a missing
|
||||
component → no write → the seed is preserved). The drainer keeps the default
|
||||
(False) so a chair's manual edit in /precedents/[id] is never clobbered.
|
||||
"""
|
||||
if isinstance(case_law_id, str):
|
||||
case_law_id = UUID(case_law_id)
|
||||
@@ -482,7 +490,7 @@ async def apply_to_record(
|
||||
# source_type/district/proceeding_type/parties). Only fill when empty so chair
|
||||
# edits in /precedents/[id] are preserved; abstains (no write) when a component
|
||||
# is missing.
|
||||
if not (record.get("citation_formatted") or "").strip():
|
||||
if force_citation or not (record.get("citation_formatted") or "").strip():
|
||||
eff = {**record, **fields_to_update}
|
||||
eff_parties = (
|
||||
fields_to_update.get("parties") or record.get("parties") or ""
|
||||
@@ -505,6 +513,7 @@ async def apply_to_record(
|
||||
async def extract_and_apply(
|
||||
case_law_id: UUID | str,
|
||||
overwrite_case_number: bool = False,
|
||||
force_citation: bool = False,
|
||||
) -> dict:
|
||||
"""Convenience wrapper: extract → merge into row → return summary."""
|
||||
suggested = await extract_metadata(case_law_id)
|
||||
@@ -523,7 +532,11 @@ async def extract_and_apply(
|
||||
"status": "extraction_failed" if has_text else "no_metadata",
|
||||
"fields": [],
|
||||
}
|
||||
result = await apply_to_record(case_law_id, suggested, overwrite_case_number=overwrite_case_number)
|
||||
result = await apply_to_record(
|
||||
case_law_id, suggested,
|
||||
overwrite_case_number=overwrite_case_number,
|
||||
force_citation=force_citation,
|
||||
)
|
||||
if result["updated"]:
|
||||
await db.recompute_searchable(case_law_id)
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user