Add context-only mode: Claude Code writes blocks, no API needed
New architecture: MCP provides context, Claude Code writes. New functions: - get_block_context(case_id, block_id) → returns full context package (prompt, source docs, claims, direction, precedents, style guide) WITHOUT calling Anthropic API - save_block_content(case_id, block_id, content) → saves block to DB New MCP tools: get_block_context, save_block_content The old write_block (API-based) still works as fallback. The new flow uses Claude Code's own model (Opus 4.6, 1M context) which has no separate API billing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -223,6 +223,26 @@ async def get_decision_template(case_number: str) -> str:
|
||||
return await drafting.get_decision_template(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_block_context(
|
||||
case_number: str,
|
||||
block_id: str,
|
||||
instructions: str = "",
|
||||
) -> str:
|
||||
"""קבלת הקשר מלא לכתיבת בלוק — ללא API. Claude Code כותב ושומר."""
|
||||
return await drafting.get_block_context(case_number, block_id, instructions)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def save_block_content(
|
||||
case_number: str,
|
||||
block_id: str,
|
||||
content: str,
|
||||
) -> str:
|
||||
"""שמירת בלוק שנכתב ע"י Claude Code ב-DB."""
|
||||
return await drafting.save_block_content(case_number, block_id, content)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def validate_decision(case_number: str) -> str:
|
||||
"""בדיקת QA — 6 בדיקות איכות על ההחלטה. אם בדיקה קריטית נכשלת — ייצוא חסום."""
|
||||
|
||||
@@ -656,6 +656,102 @@ async def _build_previous_blocks_context(case_id: UUID, decision: dict | None) -
|
||||
return "\n\n".join(parts)
|
||||
|
||||
|
||||
# ── Context-only mode (for Claude Code to write) ─────────────────
|
||||
|
||||
async def get_block_context(case_id: UUID, block_id: str, instructions: str = "") -> dict:
|
||||
"""Return full context package for a block WITHOUT calling Claude API.
|
||||
|
||||
Claude Code (or any external writer) uses this context to write the block,
|
||||
then saves it via save_block_content.
|
||||
"""
|
||||
if block_id not in BLOCK_CONFIG:
|
||||
raise ValueError(f"Unknown block: {block_id}")
|
||||
|
||||
block_cfg = BLOCK_CONFIG[block_id]
|
||||
case = await db.get_case(case_id)
|
||||
if not case:
|
||||
raise ValueError(f"Case {case_id} not found")
|
||||
|
||||
decision = await db.get_decision_by_case(case_id)
|
||||
|
||||
# Template blocks — return content directly
|
||||
if block_id in TEMPLATE_WRITERS:
|
||||
content = TEMPLATE_WRITERS[block_id](case, decision)
|
||||
return {
|
||||
"block_id": block_id,
|
||||
"title": block_cfg["title"],
|
||||
"mode": "template",
|
||||
"content": content,
|
||||
}
|
||||
|
||||
# Build all context components
|
||||
prompt_template = BLOCK_PROMPTS.get(block_id, "")
|
||||
|
||||
case_context = _build_case_context(case, decision)
|
||||
source_context = await _build_source_context(case_id, block_id)
|
||||
claims_context = await _build_claims_context(case_id)
|
||||
direction_context = _build_direction_context(decision)
|
||||
plans_context = await _build_plans_context(case_id)
|
||||
precedents_context = await _build_precedents_context(case_id, block_id)
|
||||
style_context = await _build_style_context()
|
||||
discussion_context = await _build_previous_blocks_context(case_id, decision)
|
||||
|
||||
outcome = (decision or {}).get("outcome", "rejected")
|
||||
structure_guidance = STRUCTURE_GUIDANCE.get(outcome, "")
|
||||
|
||||
formatted_prompt = prompt_template.format(
|
||||
case_context=case_context,
|
||||
source_context=source_context,
|
||||
claims_context=claims_context,
|
||||
direction_context=direction_context,
|
||||
plans_context=plans_context,
|
||||
precedents_context=precedents_context,
|
||||
style_context=style_context,
|
||||
discussion_context=discussion_context,
|
||||
structure_guidance=structure_guidance,
|
||||
)
|
||||
|
||||
if instructions:
|
||||
formatted_prompt += f"\n\n## הנחיות נוספות:\n{instructions}"
|
||||
|
||||
# Block י requires approved direction
|
||||
if block_id == "block-yod":
|
||||
dir_doc = (decision or {}).get("direction_doc") or {}
|
||||
if not dir_doc.get("approved"):
|
||||
raise ValueError("לא ניתן לכתוב בלוק דיון ללא כיוון מאושר.")
|
||||
|
||||
return {
|
||||
"block_id": block_id,
|
||||
"title": block_cfg["title"],
|
||||
"mode": "context",
|
||||
"prompt": formatted_prompt,
|
||||
"source_documents": source_context,
|
||||
"claims": claims_context,
|
||||
"direction": direction_context,
|
||||
"precedents": precedents_context,
|
||||
"style_guide": style_context,
|
||||
"previous_blocks": discussion_context,
|
||||
}
|
||||
|
||||
|
||||
async def save_block_content(case_id: UUID, block_id: str, content: str) -> dict:
|
||||
"""Save block content written by Claude Code (or any external writer)."""
|
||||
if block_id not in BLOCK_CONFIG:
|
||||
raise ValueError(f"Unknown block: {block_id}")
|
||||
|
||||
block_cfg = BLOCK_CONFIG[block_id]
|
||||
decision = await db.get_decision_by_case(case_id)
|
||||
if not decision:
|
||||
decision = await db.create_decision(case_id=case_id)
|
||||
|
||||
result = _build_result(block_id, content, block_cfg)
|
||||
result["generation_type"] = "claude-code"
|
||||
result["model_used"] = "claude-code"
|
||||
|
||||
await store_block(UUID(decision["id"]), result)
|
||||
return result
|
||||
|
||||
|
||||
# ── Renumbering ───────────────────────────────────────────────────
|
||||
|
||||
async def renumber_all_blocks(decision_id: UUID) -> dict:
|
||||
|
||||
@@ -382,6 +382,50 @@ async def export_docx(case_number: str, output_path: str = "") -> str:
|
||||
}, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def get_block_context(case_number: str, block_id: str, instructions: str = "") -> str:
|
||||
"""קבלת הקשר מלא לכתיבת בלוק — ללא קריאה ל-API. Claude Code כותב את הבלוק.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
block_id: מזהה הבלוק (block-he, block-vav, ..., block-yod-bet)
|
||||
instructions: הנחיות נוספות
|
||||
"""
|
||||
from legal_mcp.services import block_writer
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
try:
|
||||
ctx = await block_writer.get_block_context(case_id, block_id, instructions)
|
||||
return json.dumps(ctx, default=str, ensure_ascii=False, indent=2)
|
||||
except ValueError as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
async def save_block_content(case_number: str, block_id: str, content: str) -> str:
|
||||
"""שמירת בלוק שנכתב ע"י Claude Code ב-DB.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
block_id: מזהה הבלוק
|
||||
content: הטקסט שנכתב
|
||||
"""
|
||||
from legal_mcp.services import block_writer
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
try:
|
||||
result = await block_writer.save_block_content(case_id, block_id, content)
|
||||
return json.dumps(result, default=str, ensure_ascii=False, indent=2)
|
||||
except ValueError as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
async def analyze_style() -> str:
|
||||
"""הרצת ניתוח סגנון על קורפוס ההחלטות של דפנה. מחלץ דפוסי כתיבה ושומר אותם."""
|
||||
from legal_mcp.services.style_analyzer import analyze_corpus
|
||||
|
||||
Reference in New Issue
Block a user