feat(extraction): precedent metadata via Gemini Flash + scheduled drainer
The /precedents metadata queue was stuck — 24 rows requested, nothing draining them — and the agentic claude CLI hit error_max_turns on what is a single structured text→JSON task (slow + flaky). Metadata extraction is bounded extraction, the wrong fit for an agentic loop. - gemini_session.py: query_json drop-in (gemini-2.5-flash, JSON mode, httpx — no new SDK dep). Reads GEMINI_API_KEY (~/.env; SoT Infisical nautilus:/external-apis/gemini). Host-side only — no LLM from the container. - precedent_metadata_extractor: claude_session.query_json → gemini_session. Validated live: rich, accurate fields (case_name/summary/appeal_subtype/tags). - process_pending_extractions: kind-aware cooldown — metadata 2s (Gemini, fast), halacha keeps 30s (Claude rate limits). - drain_metadata_queue.py + legal-metadata-drain.config.cjs (pm2 cron */15) so the queue never clogs again. SCRIPTS.md. - X8 INV-FP5 updated: per-task engine choice (Gemini=bounded metadata, claude_session=agentic halacha), both host-side, single canonical queue (G2). Agentic/voice-sensitive work (writing, analysis, halacha) stays on claude_session (Daphna's subscription). Gemini cost ≈ $0.10/1M tokens — negligible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
| `legal-reaper.config.cjs` | pm2/js | **דמון pm2 ל-`reap_orphan_procs.py --loop`** (ברירת-מחדל 180ש', `REAP_INTERVAL_S` לעקיפה). `max_memory_restart 100M` (ה-reaper עצמו לא ידלוף). התקנה: `pm2 start scripts/legal-reaper.config.cjs && pm2 save`. לוגים: `pm2 logs legal-reaper`. | pm2 (host-side) |
|
||||
| `drain_court_fetch.py` | python | **ריקון תור-אחזור הפסיקה (X13)** — קורא ל-`court_fetch_orchestrator.drain_pending(limit)` שמוריד+קולט כל job ממתין שהיומונים מילאו, וקושר חזרה ליומון. מקומי בלבד (ingest = claude CLI). no-op מהיר כשהתור ריק. הרצה ידנית: `mcp-server/.venv/bin/python scripts/drain_court_fetch.py [limit]`. | דרך `legal-court-fetch-drain.config.cjs` (pm2 cron) |
|
||||
| `legal-court-fetch-drain.config.cjs` | pm2/js | **תזמון שעתי של `drain_court_fetch.py`** (cron `17 * * * *`, `COURT_FETCH_DRAIN_CRON` לעקיפה) — הופך את לולאת יומון→אחזור→קליטה ל-fully-autonomous. `autorestart:false` (one-shot per tick). דורש `legal-court-fetch-service` רץ. התקנה: `pm2 start scripts/legal-court-fetch-drain.config.cjs && pm2 save`. | pm2 cron (host-side) |
|
||||
| `drain_metadata_queue.py` | python | **ריקון תור חילוץ-המטא של הפסיקה** — `process_pending_extractions(kind='metadata')` ב-batches עד ריק. רץ על **Gemini Flash** (structured JSON, `gemini_session`) — מהיר ואמין, במקום ה-claude CLI ה-agentic שפגע ב-`error_max_turns`. no-op מהיר כשריק. הרצה ידנית: `mcp-server/.venv/bin/python scripts/drain_metadata_queue.py [batch]`. | דרך `legal-metadata-drain.config.cjs` (pm2 cron) |
|
||||
| `legal-metadata-drain.config.cjs` | pm2/js | **תזמון כל 15 דק' של `drain_metadata_queue.py`** (cron `*/15 * * * *`, `METADATA_DRAIN_CRON` לעקיפה) — מונע סתימה של תור חילוץ-המטא ב-/precedents. דורש `GEMINI_API_KEY` ב-`~/.env`. התקנה: `pm2 start scripts/legal-metadata-drain.config.cjs && pm2 save`. | pm2 cron (host-side) |
|
||||
| `auto-sync-cases.sh` | bash | סנכרון תיקי ערר ל-Gitea — רץ כל דקה | `* * * * *` (cron) |
|
||||
| `backup-db.sh` | bash | גיבוי PostgreSQL יומי ל-`data/backups/` (gzip) | לתזמן: `0 2 * * *` |
|
||||
| `restore-db.sh` | bash | שחזור DB מגיבוי (companion ל-backup-db.sh) | ידני |
|
||||
|
||||
48
scripts/drain_metadata_queue.py
Normal file
48
scripts/drain_metadata_queue.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""Drain the precedent metadata-extraction queue.
|
||||
|
||||
Calls ``process_pending_extractions(kind='metadata')`` in batches until the
|
||||
queue is empty (two consecutive zero-progress rounds). Metadata extraction runs
|
||||
on **Gemini Flash** (structured JSON) — fast and reliable, unlike the agentic
|
||||
claude CLI which hit ``error_max_turns`` on this bounded task. A no-op (fast)
|
||||
when the queue is empty.
|
||||
|
||||
Host-only (reads GEMINI_API_KEY + POSTGRES_URL from ~/.env via legal_mcp.config).
|
||||
Scheduled by ``legal-metadata-drain`` (pm2 cron); also runnable by hand:
|
||||
|
||||
mcp-server/.venv/bin/python scripts/drain_metadata_queue.py [batch]
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "mcp-server", "src"))
|
||||
|
||||
from legal_mcp.services import precedent_library as pl
|
||||
|
||||
|
||||
async def main() -> int:
|
||||
batch = int(sys.argv[1]) if len(sys.argv) > 1 else 10
|
||||
total = 0
|
||||
empty_rounds = 0
|
||||
rnd = 0
|
||||
while empty_rounds < 2:
|
||||
rnd += 1
|
||||
out = await pl.process_pending_extractions(kind="metadata", limit=batch)
|
||||
processed = out.get("processed", 0)
|
||||
total += processed
|
||||
print(f"[round {rnd}] processed={processed} total_pending={out.get('total_pending', 0)} "
|
||||
f"status={out.get('status')}", flush=True)
|
||||
for r in out.get("results", []):
|
||||
print(f" {str(r.get('case_number',''))[:42]}: {r.get('status')}", flush=True)
|
||||
if processed == 0:
|
||||
empty_rounds += 1
|
||||
await asyncio.sleep(3)
|
||||
else:
|
||||
empty_rounds = 0
|
||||
print(f"===DONE=== metadata extracted (cumulative cases handled={total})", flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(asyncio.run(main()))
|
||||
34
scripts/legal-metadata-drain.config.cjs
Normal file
34
scripts/legal-metadata-drain.config.cjs
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* pm2 ecosystem entry for legal-metadata-drain — scheduled (every 15 min) drain
|
||||
* of the precedent metadata-extraction queue (Gemini Flash). Keeps the
|
||||
* /precedents metadata queue from clogging (the prior agentic claude-CLI path
|
||||
* hit error_max_turns and nothing drained it autonomously).
|
||||
*
|
||||
* Pattern: cron_restart fires the script on schedule; autorestart:false → runs
|
||||
* once and exits (pm2 shows "stopped" between ticks — expected). Cheap no-op
|
||||
* when the queue is empty; Gemini Flash ≈ $0.10/1M tokens.
|
||||
*
|
||||
* Requires (host ~/.env via legal_mcp.config): GEMINI_API_KEY, POSTGRES_URL.
|
||||
*
|
||||
* Install (once):
|
||||
* pm2 start /home/chaim/legal-ai/scripts/legal-metadata-drain.config.cjs
|
||||
* pm2 save
|
||||
* Run now (manual): mcp-server/.venv/bin/python scripts/drain_metadata_queue.py
|
||||
* Schedule override: METADATA_DRAIN_CRON (default every 15 min).
|
||||
*/
|
||||
const cron = process.env.METADATA_DRAIN_CRON || "*/15 * * * *";
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "legal-metadata-drain",
|
||||
cwd: "/home/chaim/legal-ai",
|
||||
script: "/home/chaim/legal-ai/mcp-server/.venv/bin/python",
|
||||
args: "scripts/drain_metadata_queue.py 10",
|
||||
env: { HOME: "/home/chaim", PYTHONUNBUFFERED: "1" },
|
||||
autorestart: false, // one-shot per cron tick
|
||||
cron_restart: cron,
|
||||
max_memory_restart: "500M",
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user