Phase 4 complete: Steps 21, 22, 24 — Korban external integration, L2 Urim v'Tumim, Kushya generator
Vayikra 9:24 — vatetzei esh milifnei Hashem vatokhal al hamizbeach.
Phase 4 of the architecture is now complete. The remaining three steps
that were marked "blocked on external infrastructure" are built —
discipline-respecting, eish-zarah-aware, and tested.
## Step 22 — Korban Processor external model integration
(korban_processor/src/korban_processor/external_offering.py)
The single legitimate entry point for external model consultation.
Wraps Claude API calls through the offering protocol so external
output is treated as korban (raw material), never authoritative:
- semichah — provenance is tagged (avodah zarah Form 1 prevention)
- shechitah — assistant voice, hedging, formatting stripped
- zerikah — vital content extracted
- haktarah — converted to Prolog propositions
- route — sent to internal component per offering type
The raw Korban is preserved on the ProcessedKorban (gilui arayot
Form 3 prevention — input/output coupling never severed).
No internal component should call call_claude() directly.
11 tests, mocked external calls. Live API test opt-in via
SHORASHIM_LIVE_API=1.
## Step 24 — L2 Urim v'Tumim interface
(vav_pipeline/lib/vav_pipeline/urim_v_tumim.ex)
PRD §4.7: housed in the kohen gadol's breastplate; consulted only
on high-stakes decisions where normal deliberation is insufficient.
Bamidbar 27:21 — al-piv yetz'u v'al-piv yavo'u.
Three escalation steps, three honest outcomes:
1. resolved_by_specification — re-read produced an answer
2. resolved_by_external — external testimony recorded (NOT binding)
3. deferred_to_human — the system stops, asks for human judgment
Pre-condition gating refuses cases that didn't reach Sanhedrin teiku
first (no preempting deliberation) or that aren't flagged high-stakes
(ordinary teiku stays teiku). Records every consultation with steps
taken — there is no quiet escalation.
Per PRD: "The Urim v'Tumim never fabricates certainty where none
exists." When external also fails, the system stops. It does not
proceed on a guess.
Added to VavPipeline supervision tree. 14 tests.
## Step 21 — Kushya generator scaffolding
(kushya_generator/)
Directory layout per Architecture §3.6 with three layers:
- data/training_pairs/seed_pairs.jsonl — 10 hand-curated bootstrap
pairs covering 9 kushya types (scope_narrowing, internal_tension,
harmonization, exception_test, edge_case, scope_application,
rabbinic_constraint, method_question, subject_identification)
- data/prepare_training.py — validates the four kushya requirements
(specific Torah passage, identified weakness, question form,
non-trivial), produces train.jsonl + eval.jsonl with stratified
80/20 split by type
- fine_tune/config.yaml + train.py — QLoRA configuration and
training script. Operator's task — needs GPU. Script is
complete and self-contained.
- inference/kushya_api.py — three-backend interface
(fine_tuned → external → fallback). When fine-tuned model isn't
deployed, falls back to external via Korban Processor (Step 22).
The fallback is honest: returns no kushya rather than inventing
one. The Talmudic Engine's rule-based challenge.pl remains the
primary kushya source.
17 tests covering dataset validation, backend selection, and
reference extraction.
## .gitignore fix
The earlier "data/" line was too broad — it caught
kushya_generator/data/. Scoped to /data/ (root-level only).
## Test counts
- Elixir: 274 (was 260; +14 for UrimVTumim)
- Korban Python: 196 + 1 skipped (was 195; +1 module = 11 tests)
- Kushya Python: 17 (new)
- Haskell: 33 (unchanged)
- Prolog: 195 (unchanged)
- Total: 715 + 19 L3 excluded
## Phase 4 status
All 12 steps now have implementations:
- Steps 23, 25, 26, 27, 28, 29, 30, 31, 32: complete
- Steps 21, 22, 24: complete with discipline preserved
Step 21's fine-tuning run remains the operator's GPU task; the
pipeline is built such that a deployed adapter slots into the
inference backend without touching the Talmudic Engine or anything
above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d4d0a6fcb6
commit
e223ab15b3
15 files changed
+2003
-1
No files matched your search
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
External offering — the flow from outbound query to processed korban.
|
||||
|
||||
Architecture §4 Step 22: Korban Processor integration with external models.
|
||||
PRD §5.1: The system reasons, it does not predict. External model output is
|
||||
korban (raw material), not instruction. Every external response MUST pass
|
||||
through the offering protocol before any internal component touches it.
|
||||
|
||||
Eish zarah discipline (specs/eish_zarah_reading.md):
|
||||
- External output is tagged with its provenance — the source is never
|
||||
concealed (Form 1: avodah zarah is treating external output as if it
|
||||
were Torah).
|
||||
- The raw korban is preserved on the ProcessedKorban (Form 3: gilui
|
||||
arayot is severing output from its source).
|
||||
- The coupling_trace records that propositions derived from external
|
||||
output are NOT Torah-grounded — only Torah-coupled when the Talmudic
|
||||
Engine later grounds them via Torah verses.
|
||||
- A confidence value is never assigned to external content at this
|
||||
boundary (Form 2: golden calf is confident-but-uncoupled output).
|
||||
|
||||
This module is the only entry point for external model consultation.
|
||||
No internal component calls an external model directly.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from korban_processor.korban import (
|
||||
Korban,
|
||||
ProcessedKorban,
|
||||
OfferingType,
|
||||
Provenance,
|
||||
)
|
||||
from korban_processor.model_interface import (
|
||||
ModelRequest,
|
||||
ModelResponse,
|
||||
call_claude,
|
||||
)
|
||||
from korban_processor.semichah import semichah
|
||||
from korban_processor.shechitah import shechitah
|
||||
from korban_processor.zerikah import zerikah
|
||||
from korban_processor.haktarah import haktarah
|
||||
from korban_processor.router import RoutingResult, route
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExternalConsultation:
|
||||
"""The full record of an external-model consultation.
|
||||
|
||||
All four fields are preserved so the full chain is auditable:
|
||||
- request: what was asked and with what system prompt
|
||||
- raw_response: the unprocessed output from the external model
|
||||
- processed: the korban after the offering protocol
|
||||
- routing: where the processed korban was sent
|
||||
"""
|
||||
request: ModelRequest
|
||||
raw_response: ModelResponse
|
||||
processed: ProcessedKorban
|
||||
routing: RoutingResult
|
||||
|
||||
|
||||
def consult_external_model(
|
||||
query: str,
|
||||
source_query: str,
|
||||
provenance: Provenance = Provenance.CLAUDE,
|
||||
offering_type: OfferingType = OfferingType.OLAH,
|
||||
system_prompt: Optional[str] = None,
|
||||
caller: Optional[callable] = None,
|
||||
) -> ExternalConsultation:
|
||||
"""Submit a query to an external model, then process the response as korban.
|
||||
|
||||
Args:
|
||||
query: The text sent to the external model.
|
||||
source_query: The upstream question that triggered this consultation
|
||||
(e.g. the user's courtyard request). Stored on the Korban so the
|
||||
chain from user → system → external → processed is auditable.
|
||||
provenance: Which external model. Claude is the default.
|
||||
offering_type: How to route the processed output. OLAH means the
|
||||
output is fully consumed into the Talmudic Engine.
|
||||
system_prompt: Optional system prompt for the external model.
|
||||
caller: Injectable model caller for testing. Defaults to call_claude.
|
||||
Must accept a ModelRequest and return a ModelResponse.
|
||||
|
||||
Returns:
|
||||
ExternalConsultation with request, raw response, processed korban, and
|
||||
routing result.
|
||||
|
||||
Note: This function is the ONLY legitimate entry point for external model
|
||||
consultation. No internal component should call call_claude() directly.
|
||||
Everything external passes through the offering protocol.
|
||||
"""
|
||||
if provenance is not Provenance.CLAUDE and caller is None:
|
||||
# The current model_interface only implements Claude. Other providers
|
||||
# must supply an explicit caller — we refuse to silently proxy.
|
||||
raise NotImplementedError(
|
||||
f"provenance={provenance.value} requires an explicit caller; "
|
||||
f"only Claude is wired in model_interface.call_claude"
|
||||
)
|
||||
|
||||
call = caller or call_claude
|
||||
|
||||
request = ModelRequest(
|
||||
query=query,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
|
||||
raw_response = call(request)
|
||||
|
||||
# The raw response is now an external output. From here on, it is korban
|
||||
# — raw material to be processed. It is NOT Torah. It is NOT authoritative.
|
||||
# The offering protocol consumes the external form and extracts whatever
|
||||
# Torah-adjacent content might be inside.
|
||||
korban = semichah(
|
||||
content=raw_response.content,
|
||||
source=provenance,
|
||||
query=source_query,
|
||||
offering_type=offering_type,
|
||||
)
|
||||
|
||||
terminated = shechitah(korban)
|
||||
vital, discarded = zerikah(terminated, korban)
|
||||
propositions = haktarah(vital)
|
||||
|
||||
processed = ProcessedKorban(
|
||||
vital_content=vital,
|
||||
propositions=propositions,
|
||||
provenance=provenance,
|
||||
offering_type=offering_type,
|
||||
source_query=source_query,
|
||||
discarded=discarded,
|
||||
raw_korban=korban,
|
||||
)
|
||||
|
||||
routing = route(processed, offering_type)
|
||||
|
||||
return ExternalConsultation(
|
||||
request=request,
|
||||
raw_response=raw_response,
|
||||
processed=processed,
|
||||
routing=routing,
|
||||
)
|
||||
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
Tests for external_offering — the orchestrated outbound consultation flow.
|
||||
|
||||
Architecture §4 Step 22.
|
||||
|
||||
These tests use a mock model caller so no real API requests are made.
|
||||
A separate live integration test (skipped by default) can run against
|
||||
Anthropic's API when ANTHROPIC_API_KEY is set and the SHORASHIM_LIVE_API
|
||||
env var is true.
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from korban_processor.external_offering import (
|
||||
ExternalConsultation,
|
||||
consult_external_model,
|
||||
)
|
||||
from korban_processor.korban import OfferingType, Provenance
|
||||
from korban_processor.model_interface import ModelRequest, ModelResponse
|
||||
|
||||
|
||||
def _fake_caller(response_text: str):
|
||||
"""Build a mock caller that returns a fixed response."""
|
||||
def caller(request: ModelRequest) -> ModelResponse:
|
||||
return ModelResponse(
|
||||
content=response_text,
|
||||
model="mock-claude",
|
||||
stop_reason="end_turn",
|
||||
input_tokens=10,
|
||||
output_tokens=20,
|
||||
)
|
||||
return caller
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Basic flow
|
||||
# ============================================================
|
||||
|
||||
def test_consult_external_model_returns_full_consultation():
|
||||
"""A consultation captures request, raw response, processed korban,
|
||||
and routing — the full chain is auditable."""
|
||||
fake_response = (
|
||||
"Bereishit 1:1 begins with the root bara. The shoresh ב-ר-א means "
|
||||
"to create from nothing. This is foundational."
|
||||
)
|
||||
result = consult_external_model(
|
||||
query="Explain bereishit 1:1",
|
||||
source_query="user_courtyard_request_42",
|
||||
caller=_fake_caller(fake_response),
|
||||
)
|
||||
|
||||
assert isinstance(result, ExternalConsultation)
|
||||
assert result.request.query == "Explain bereishit 1:1"
|
||||
assert result.raw_response.content == fake_response
|
||||
assert result.raw_response.model == "mock-claude"
|
||||
assert result.processed.provenance is Provenance.CLAUDE
|
||||
assert result.routing.destination == "talmudic_engine" # default OLAH
|
||||
|
||||
|
||||
def test_raw_korban_is_preserved_on_processed_output():
|
||||
"""Eish zarah Form 3 (gilui arayot — severed coupling): the raw
|
||||
korban must be preserved on the processed output. The floor between
|
||||
input and output is never severed."""
|
||||
result = consult_external_model(
|
||||
query="any",
|
||||
source_query="trigger_query",
|
||||
caller=_fake_caller(
|
||||
"The root k-d-sh appears in Vayikra. Holiness is separation."
|
||||
),
|
||||
)
|
||||
|
||||
raw = result.processed.raw_korban
|
||||
assert raw is not None, (
|
||||
"raw_korban must be preserved on ProcessedKorban — "
|
||||
"severing input from output is gilui arayot"
|
||||
)
|
||||
assert raw.source_query == "trigger_query"
|
||||
assert raw.provenance is Provenance.CLAUDE
|
||||
|
||||
|
||||
def test_provenance_is_recorded_on_korban():
|
||||
"""Eish zarah Form 1 (avodah zarah — external as Torah): provenance
|
||||
is never concealed. Every external response is tagged with its source."""
|
||||
result = consult_external_model(
|
||||
query="any",
|
||||
source_query="any",
|
||||
provenance=Provenance.CLAUDE,
|
||||
caller=_fake_caller("test response with bereishit reference"),
|
||||
)
|
||||
assert result.processed.provenance is Provenance.CLAUDE
|
||||
assert result.processed.raw_korban.provenance is Provenance.CLAUDE
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Offering type → routing destination
|
||||
# ============================================================
|
||||
|
||||
@pytest.mark.parametrize("offering_type,destination", [
|
||||
(OfferingType.OLAH, "talmudic_engine"),
|
||||
(OfferingType.SHELAMIM, "talmudic_engine"),
|
||||
(OfferingType.MINCHAH, "talmudic_engine"),
|
||||
(OfferingType.CHATAT, "error_correction"),
|
||||
])
|
||||
def test_offering_type_drives_routing(offering_type, destination):
|
||||
"""Each offering type routes to its specified destination."""
|
||||
result = consult_external_model(
|
||||
query="any",
|
||||
source_query="any",
|
||||
offering_type=offering_type,
|
||||
caller=_fake_caller(
|
||||
"The shoresh r-tz-h means will. Vayikra 1:3 uses lirtzono — "
|
||||
"for his acceptance."
|
||||
),
|
||||
)
|
||||
assert result.routing.destination == destination
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Refusal to silently proxy unknown providers
|
||||
# ============================================================
|
||||
|
||||
def test_unknown_provenance_without_caller_raises():
|
||||
"""We don't silently route MISTRAL/GPT/etc through Claude. A caller
|
||||
must be supplied explicitly. This prevents accidental misrouting and
|
||||
keeps provenance honest."""
|
||||
with pytest.raises(NotImplementedError) as excinfo:
|
||||
consult_external_model(
|
||||
query="any",
|
||||
source_query="any",
|
||||
provenance=Provenance.GPT,
|
||||
)
|
||||
assert "GPT" in str(excinfo.value).upper() or "gpt" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_explicit_caller_overrides_provider_check():
|
||||
"""If the caller supplies a callable for a non-Claude provider, the flow
|
||||
proceeds — they took responsibility for the wiring."""
|
||||
result = consult_external_model(
|
||||
query="any",
|
||||
source_query="any",
|
||||
provenance=Provenance.GPT,
|
||||
caller=_fake_caller("test response with shoresh sh-l-m"),
|
||||
)
|
||||
assert result.processed.provenance is Provenance.GPT
|
||||
|
||||
|
||||
# ============================================================
|
||||
# System prompt is forwarded
|
||||
# ============================================================
|
||||
|
||||
def test_system_prompt_is_forwarded():
|
||||
"""A system prompt sent in becomes part of the request record."""
|
||||
result = consult_external_model(
|
||||
query="any",
|
||||
source_query="any",
|
||||
system_prompt="You are a Torah scholar. Cite verse references.",
|
||||
caller=_fake_caller("Bereishit 1:1 is the opening verse."),
|
||||
)
|
||||
assert result.request.system_prompt == "You are a Torah scholar. Cite verse references."
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Live API integration test — opt-in only
|
||||
# ============================================================
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("SHORASHIM_LIVE_API"),
|
||||
reason="Set SHORASHIM_LIVE_API=1 to run a real API call against Anthropic",
|
||||
)
|
||||
def test_live_anthropic_call():
|
||||
"""Real API call. Costs money. Run only when explicitly enabled."""
|
||||
if not os.environ.get("ANTHROPIC_API_KEY"):
|
||||
pytest.skip("ANTHROPIC_API_KEY not set")
|
||||
|
||||
result = consult_external_model(
|
||||
query="What is the shoresh of bereishit?",
|
||||
source_query="live_integration_test",
|
||||
offering_type=OfferingType.OLAH,
|
||||
)
|
||||
|
||||
assert result.raw_response.content
|
||||
assert result.processed.raw_korban is not None
|
||||
assert result.processed.provenance is Provenance.CLAUDE
|
||||
assert result.raw_response.model.startswith("claude")
|
||||
Reference in new issue
Block a user