Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
861cd4e64e | ||
|
|
4e0eb2f983 | ||
|
|
ab6ca1ebcf |
No files matched your search
@@ -63,6 +63,11 @@ if [[ "${DOORPOST_SKIP:-0}" != "1" ]]; then
|
|||||||
python3 -I scripts/doorpost_check.py || exit 1
|
python3 -I scripts/doorpost_check.py || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The engine's verse facts are derived from L1 (scripts/build_engine_verses.py);
|
||||||
|
# an image whose facts lag the scroll reasons over a scroll that is not
|
||||||
|
# the one the pipeline reads. Regenerate and commit, never skip.
|
||||||
|
python3 -I scripts/build_engine_verses.py --check || exit 1
|
||||||
|
|
||||||
if ! docker buildx version >/dev/null 2>&1; then
|
if ! docker buildx version >/dev/null 2>&1; then
|
||||||
echo "ERROR: docker buildx not available — install Docker Desktop or buildx plugin" >&2
|
echo "ERROR: docker buildx not available — install Docker Desktop or buildx plugin" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Executable
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""build_engine_verses.py — the Talmudic Engine's verse facts, derived from L1.
|
||||||
|
|
||||||
|
Architecture §3.4: verse(Book, Chapter, Verse, HebrewRoots, Gloss) is "the
|
||||||
|
reasoning layer's view of the specification — not a replacement for L1".
|
||||||
|
Until 2026-10-19 that view was 32 hand-written verses, so any root the
|
||||||
|
engine knew lit the same handful and every reading cited the same list.
|
||||||
|
This script writes talmudic_engine/src/specification/l1_verses.pl from
|
||||||
|
l1/torah — every verse, its roots as the engine names them
|
||||||
|
(Hebrew shoresh → letter names, finals folded: ברא → bet_resh_aleph), the
|
||||||
|
consonantal text as the gloss — and records the SHA-256 of the L1 files it
|
||||||
|
read in the header, so the facts can be checked against the scroll.
|
||||||
|
|
||||||
|
scripts/build_engine_verses.py # write the facts
|
||||||
|
scripts/build_engine_verses.py --check # exit 1 if the facts do not match L1 as it stands
|
||||||
|
|
||||||
|
Devarim 4:2 — nothing added, nothing taken: the engine reads the scroll's
|
||||||
|
roots, not the builder's memory of them.
|
||||||
|
"""
|
||||||
|
import argparse, hashlib, json, os, sys
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
L1 = os.path.join(ROOT, "l1", "torah")
|
||||||
|
OUT = os.path.join(ROOT, "talmudic_engine", "src", "specification", "l1_verses.pl")
|
||||||
|
BOOKS = ["bereishit", "shemot", "vayikra", "bamidbar", "devarim"]
|
||||||
|
|
||||||
|
NAMES = {
|
||||||
|
"א": "aleph", "ב": "bet", "ג": "gimel", "ד": "dalet", "ה": "he", "ו": "vav", "ז": "zayin",
|
||||||
|
"ח": "chet", "ט": "tet", "י": "yod", "כ": "kaf", "ך": "kaf", "ל": "lamed", "מ": "mem", "ם": "mem",
|
||||||
|
"נ": "nun", "ן": "nun", "ס": "samekh", "ע": "ayin", "פ": "pe", "ף": "pe", "צ": "tsadi", "ץ": "tsadi",
|
||||||
|
"ק": "qof", "ר": "resh", "ש": "shin", "ת": "tav",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def root_atom(shoresh):
|
||||||
|
if not shoresh:
|
||||||
|
return None
|
||||||
|
names = [NAMES.get(c) for c in shoresh]
|
||||||
|
if not names or any(n is None for n in names):
|
||||||
|
return None
|
||||||
|
return "_".join(names)
|
||||||
|
|
||||||
|
|
||||||
|
def quote(s):
|
||||||
|
return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'"
|
||||||
|
|
||||||
|
|
||||||
|
def verse_files():
|
||||||
|
out = []
|
||||||
|
for book in BOOKS:
|
||||||
|
bdir = os.path.join(L1, book)
|
||||||
|
for ch in sorted((d for d in os.listdir(bdir) if d.isdigit()), key=int):
|
||||||
|
cdir = os.path.join(bdir, ch)
|
||||||
|
for f in sorted((f for f in os.listdir(cdir) if f.endswith(".json") and f[:-5].isdigit()), key=lambda x: int(x[:-5])):
|
||||||
|
out.append((book, int(ch), int(f.split(".")[0]), os.path.join(cdir, f)))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def build():
|
||||||
|
h = hashlib.sha256()
|
||||||
|
facts = []
|
||||||
|
nverses = nroots = 0
|
||||||
|
for book, ch, vs, path in verse_files():
|
||||||
|
data = open(path, "rb").read()
|
||||||
|
h.update(f"{book}/{ch}/{vs}\n".encode())
|
||||||
|
h.update(data)
|
||||||
|
d = json.loads(data)
|
||||||
|
roots = []
|
||||||
|
for w in d.get("words", []):
|
||||||
|
a = root_atom(w.get("shoresh"))
|
||||||
|
if a and a not in roots:
|
||||||
|
roots.append(a)
|
||||||
|
gloss = " ".join(w.get("consonants", "") for w in d.get("words", []) if w.get("consonants"))
|
||||||
|
facts.append(f"verse({book}, {ch}, {vs}, [{', '.join(roots)}], {quote(gloss)}).")
|
||||||
|
nverses += 1
|
||||||
|
nroots += len(roots)
|
||||||
|
header = f"""%% l1_verses.pl — GENERATED by scripts/build_engine_verses.py from l1/torah.
|
||||||
|
%% Do not edit: regenerate. verse(Book, Chapter, Verse, Roots, Gloss) for
|
||||||
|
%% every verse of L1; Roots are the engine's letter-name atoms derived from
|
||||||
|
%% each word's shoresh (finals folded), once per verse in word order; Gloss
|
||||||
|
%% is the consonantal text. Included by torah_text.pl.
|
||||||
|
%%
|
||||||
|
%% l1_sha256: {h.hexdigest()}
|
||||||
|
%% verses: {nverses} root_occurrences: {nroots}
|
||||||
|
"""
|
||||||
|
return header + "\n".join(facts) + "\n", h.hexdigest(), nverses
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--check", action="store_true")
|
||||||
|
a = ap.parse_args()
|
||||||
|
text, digest, n = build()
|
||||||
|
if a.check:
|
||||||
|
try:
|
||||||
|
current = open(OUT, encoding="utf-8").read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"engine verses: {OUT} missing — run scripts/build_engine_verses.py", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if current != text:
|
||||||
|
print(f"engine verses: {OUT} does not match L1 (l1_sha256 now {digest[:12]}…) — regenerate", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
print(f"engine verses: match L1 ({n} verses, l1_sha256 {digest[:12]}…)")
|
||||||
|
return
|
||||||
|
with open(OUT, "w", encoding="utf-8") as f:
|
||||||
|
f.write(text)
|
||||||
|
print(f"wrote {OUT}: {n} verses, l1_sha256 {digest[:12]}…")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -111,16 +111,45 @@ find_relevant_mitzvot_traced(case(_Ref, Domain, Roots, _Question), Mitzvot, Coup
|
|||||||
find_relevant_verses(case(Ref, Domain, Roots, Question), Verses) :-
|
find_relevant_verses(case(Ref, Domain, Roots, Question), Verses) :-
|
||||||
find_relevant_verses_traced(case(Ref, Domain, Roots, Question), Verses, _).
|
find_relevant_verses_traced(case(Ref, Domain, Roots, Question), Verses, _).
|
||||||
|
|
||||||
%% Traced version: returns verses AND the coupling trace for each
|
%% Traced version: returns verses AND the coupling trace for each.
|
||||||
|
%%
|
||||||
|
%% Over the whole scroll (2026-10-19, l1_verses.pl) a common root lights
|
||||||
|
%% hundreds of verses; a reading that cites them all cites nothing. The
|
||||||
|
%% verses are ranked by how many of the case's roots they carry — a
|
||||||
|
%% verse sharing three roots stands closer to the case than one sharing
|
||||||
|
%% one — and the reading keeps the top ?MAX_VERSES, ties broken by
|
||||||
|
%% canonical order. The coupling trace is kept only for the verses the
|
||||||
|
%% reading cites: a trace names what the reading stands on.
|
||||||
|
max_verses(12).
|
||||||
|
|
||||||
find_relevant_verses_traced(case(_Ref, _Domain, Roots, _Question), Verses, Couplings) :-
|
find_relevant_verses_traced(case(_Ref, _Domain, Roots, _Question), Verses, Couplings) :-
|
||||||
findall(verse_ref(Book, Ch, Vs, Gloss)-coupled(root(Root), verse(Book, Ch, Vs)),
|
sort(Roots, UniqueRoots),
|
||||||
( member(Root, Roots),
|
findall(Shared-verse_ref(Book, Ch, Vs, Gloss),
|
||||||
verse(Book, Ch, Vs, VerseRoots, Gloss),
|
( verse(Book, Ch, Vs, VerseRoots, Gloss),
|
||||||
|
intersection(UniqueRoots, VerseRoots, SharedRoots),
|
||||||
|
SharedRoots \== [],
|
||||||
|
length(SharedRoots, N),
|
||||||
|
Shared is -N
|
||||||
|
),
|
||||||
|
Scored),
|
||||||
|
%% keysort is stable: equal scores keep the scroll's order
|
||||||
|
keysort(Scored, Ranked),
|
||||||
|
pairs_values(Ranked, RankedVerses),
|
||||||
|
max_verses(Max),
|
||||||
|
take(Max, RankedVerses, Verses),
|
||||||
|
findall(coupled(root(Root), verse(Book, Ch, Vs)),
|
||||||
|
( member(verse_ref(Book, Ch, Vs, _), Verses),
|
||||||
|
verse(Book, Ch, Vs, VerseRoots, _),
|
||||||
|
member(Root, UniqueRoots),
|
||||||
member(Root, VerseRoots)
|
member(Root, VerseRoots)
|
||||||
),
|
),
|
||||||
AllPairs),
|
Couplings).
|
||||||
pairs_keys_values(AllPairs, AllVerses, Couplings),
|
|
||||||
sort(AllVerses, Verses).
|
take(N, List, Prefix) :-
|
||||||
|
length(List, L),
|
||||||
|
( L =< N -> Prefix = List
|
||||||
|
; length(Prefix, N), append(Prefix, _, List)
|
||||||
|
).
|
||||||
|
|
||||||
%% ============================================================
|
%% ============================================================
|
||||||
%% CONSTRUCT INTERPRETATION
|
%% CONSTRUCT INTERPRETATION
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -16,93 +16,19 @@
|
|||||||
]).
|
]).
|
||||||
|
|
||||||
%% ============================================================
|
%% ============================================================
|
||||||
%% BEREISHIT — Creation narratives
|
%% THE SCROLL — every verse of L1, generated (2026-10-19)
|
||||||
%% ============================================================
|
%% ============================================================
|
||||||
|
%%
|
||||||
|
%% The 32 hand-written verses that stood here from Phase 1 are gone:
|
||||||
|
%% with them, any root the engine knew lit the same handful and every
|
||||||
|
%% reading cited the same list. l1_verses.pl is written by
|
||||||
|
%% scripts/build_engine_verses.py from l1/torah — 5,853 verses, each
|
||||||
|
%% word's shoresh as the engine names it, the consonantal text as the
|
||||||
|
%% gloss — and carries the SHA-256 of the L1 it read. Check it against
|
||||||
|
%% the scroll with `scripts/build_engine_verses.py --check`.
|
||||||
|
|
||||||
verse(bereishit, 1, 1, [bet_resh_aleph, bet_resh_aleph, aleph_lamed_he],
|
:- include('l1_verses.pl').
|
||||||
'In the beginning God created the heavens and the earth').
|
|
||||||
verse(bereishit, 1, 3, [aleph_mem_resh, aleph_lamed_he, he_yod_he, aleph_vav_resh],
|
|
||||||
'And God said let there be light and there was light').
|
|
||||||
verse(bereishit, 1, 4, [resh_aleph_he, aleph_lamed_he, aleph_vav_resh, tet_vav_bet, bet_dalet_lamed],
|
|
||||||
'And God saw that the light was good and God separated').
|
|
||||||
verse(bereishit, 1, 27, [bet_resh_aleph, aleph_lamed_he, aleph_dalet_mem, tzade_lamed_mem, bet_resh_aleph],
|
|
||||||
'And God created man in His image in the image of God He created him').
|
|
||||||
verse(bereishit, 2, 2, [kaf_lamed_he, aleph_lamed_he, yod_vav_mem, shin_bet_tav, mem_lamed_aleph_kaf_he],
|
|
||||||
'And on the seventh day God completed His work and He rested').
|
|
||||||
verse(bereishit, 2, 3, [bet_resh_kaf, aleph_lamed_he, yod_vav_mem, shin_bet_tav, qof_dalet_shin],
|
|
||||||
'And God blessed the seventh day and sanctified it').
|
|
||||||
verse(bereishit, 9, 6, [shin_pe_kaf, dalet_mem, aleph_dalet_mem, tzade_lamed_mem, aleph_lamed_he],
|
|
||||||
'Whoever sheds the blood of man by man shall his blood be shed for in the image of God He made man').
|
|
||||||
|
|
||||||
%% ============================================================
|
|
||||||
%% SHEMOT — The ten dibrot and the covenant
|
|
||||||
%% ============================================================
|
|
||||||
|
|
||||||
verse(shemot, 3, 14, [aleph_he_yod_he, aleph_mem_resh, aleph_he_yod_he],
|
|
||||||
'I am that I am').
|
|
||||||
verse(shemot, 20, 2, [aleph_nun_kaf_yod, he_shin_mem, aleph_lamed_he],
|
|
||||||
'I am the LORD your God who brought you out of the land of Egypt').
|
|
||||||
verse(shemot, 20, 3, [he_yod_he, aleph_lamed_he, aleph_chet_resh],
|
|
||||||
'You shall have no other gods before Me').
|
|
||||||
verse(shemot, 20, 8, [zayin_kaf_resh, yod_vav_mem, shin_bet_tav, qof_dalet_shin],
|
|
||||||
'Remember the Sabbath day to sanctify it').
|
|
||||||
verse(shemot, 20, 13, [resh_tzade_chet],
|
|
||||||
'You shall not murder').
|
|
||||||
verse(shemot, 20, 14, [nun_aleph_pe],
|
|
||||||
'You shall not commit adultery').
|
|
||||||
verse(shemot, 20, 15, [gimel_nun_bet],
|
|
||||||
'You shall not steal').
|
|
||||||
verse(shemot, 20, 16, [ayin_nun_he, shin_qof_resh, ayin_dalet],
|
|
||||||
'You shall not bear false witness against your neighbor').
|
|
||||||
verse(shemot, 20, 17, [chet_mem_dalet],
|
|
||||||
'You shall not covet').
|
|
||||||
|
|
||||||
%% ============================================================
|
|
||||||
%% VAYIKRA — Holiness and offerings
|
|
||||||
%% ============================================================
|
|
||||||
|
|
||||||
verse(vayikra, 1, 3, [qof_resh_bet, ayin_lamed_he, tav_mem_yod_mem],
|
|
||||||
'If his offering is a burnt offering let him offer a male without blemish').
|
|
||||||
verse(vayikra, 4, 27, [chet_tet_aleph, nun_pe_shin, shin_gimel_gimel_he],
|
|
||||||
'If anyone of the common people sins unintentionally').
|
|
||||||
verse(vayikra, 19, 2, [qof_dalet_shin, he_yod_he, qof_dalet_shin],
|
|
||||||
'Be holy for I the LORD your God am holy').
|
|
||||||
verse(vayikra, 19, 15, [ayin_shin_he, mem_shin_pe_tet, tzade_dalet_qof],
|
|
||||||
'In righteousness shall you judge your neighbor').
|
|
||||||
verse(vayikra, 19, 18, [aleph_he_bet, resh_ayin, kaf_mem_vav],
|
|
||||||
'You shall love your neighbor as yourself').
|
|
||||||
|
|
||||||
%% ============================================================
|
|
||||||
%% BAMIDBAR — The camp and the cloud
|
|
||||||
%% ============================================================
|
|
||||||
|
|
||||||
verse(bamidbar, 9, 23, [pe_he, he_shin_mem, chet_nun_he, pe_he, he_shin_mem, nun_samekh_ayin],
|
|
||||||
'At the mouth of the LORD they camped and at the mouth of the LORD they journeyed').
|
|
||||||
verse(bamidbar, 10, 2, [ayin_shin_he, shin_nun_yod, chet_tzade_tzade_resh_he, kaf_samekh_pe],
|
|
||||||
'Make two silver trumpets for the calling of the assembly').
|
|
||||||
verse(bamidbar, 35, 11, [mem_qof_lamed_tet, ayin_yod_resh, nun_pe_shin, resh_tzade_chet, shin_gimel_gimel_he],
|
|
||||||
'You shall designate cities of refuge for one who kills a person unintentionally').
|
|
||||||
|
|
||||||
%% ============================================================
|
|
||||||
%% DEVARIM — Moses' final instruction
|
|
||||||
%% ============================================================
|
|
||||||
|
|
||||||
verse(devarim, 6, 4, [shin_mem_ayin, yod_shin_resh_aleph_lamed, he_shin_mem, aleph_lamed_he, he_shin_mem, aleph_chet_dalet],
|
|
||||||
'Hear O Israel the LORD our God the LORD is one').
|
|
||||||
verse(devarim, 6, 5, [aleph_he_bet, he_shin_mem, aleph_lamed_he, kaf_lamed, lamed_bet_bet],
|
|
||||||
'And you shall love the LORD your God with all your heart').
|
|
||||||
verse(devarim, 16, 18, [shin_pe_tet, shin_tet_resh, nun_tav_nun, shin_ayin_resh],
|
|
||||||
'Judges and officers shall you appoint in all your gates').
|
|
||||||
verse(devarim, 16, 20, [tzade_dalet_qof, tzade_dalet_qof, resh_dalet_pe],
|
|
||||||
'Justice justice shall you pursue').
|
|
||||||
verse(devarim, 21, 1, [nun_pe_lamed, chet_lamed_lamed, shin_dalet_he, yod_dalet_ayin],
|
|
||||||
'If a slain person is found in the field and it is not known who struck him').
|
|
||||||
verse(devarim, 25, 12, [nun_tzade_lamed, resh_dalet_pe],
|
|
||||||
'You shall save the pursued from the pursuer').
|
|
||||||
verse(devarim, 26, 2, [lamed_qof_chet, resh_aleph_shin_yod_tav, pe_resh_yod, aleph_dalet_mem_he],
|
|
||||||
'You shall take of the first of all the fruit of the ground').
|
|
||||||
verse(devarim, 26, 5, [aleph_resh_mem_yod, aleph_bet_dalet, aleph_bet_yod],
|
|
||||||
'A wandering Aramean was my father').
|
|
||||||
|
|
||||||
%% ============================================================
|
%% ============================================================
|
||||||
%% QUERIES
|
%% QUERIES
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
alias VavPipeline.{Specification, CycleLog, Manna, HaltConditions,
|
alias VavPipeline.{Specification, CycleLog, Manna, HaltConditions,
|
||||||
ShoreshEngineClient, TalmudicEngineClient, SanhedrinClient,
|
ShoreshEngineClient, TalmudicEngineClient, TalmudicEngineSocket,
|
||||||
Chatzotzrot}
|
SanhedrinClient, Chatzotzrot}
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@@ -308,7 +308,11 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
final =
|
final =
|
||||||
case result do
|
case result do
|
||||||
{:ok, data} ->
|
{:ok, data} ->
|
||||||
check_halt_on_output(data)
|
# Every question is read against the engine, not only a teiku
|
||||||
|
# that never comes (2026-10-19: ki_tov returns teiku only on a
|
||||||
|
# root with no Hebrew, and the Shoresh Engine never leaves one;
|
||||||
|
# the Talmudic route had never been walked by a real question).
|
||||||
|
data |> consult_engine(context) |> check_halt_on_output()
|
||||||
|
|
||||||
{:unresolved, data} ->
|
{:unresolved, data} ->
|
||||||
case route_if_unresolved(data, context) do
|
case route_if_unresolved(data, context) do
|
||||||
@@ -325,6 +329,90 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
final
|
final
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# The engine, consulted on every resolved cycle
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# Architecture §3.3: the Talmudic Engine grounds a reading in verses
|
||||||
|
# and mitzvot over the case's roots. The cycle's own ki_tov says the
|
||||||
|
# roots stand in L1; the engine says what stands on them. Called
|
||||||
|
# directly on the socket — never the client's stub (tablet 41): an
|
||||||
|
# engine that cannot be reached is reported as not consulted, and the
|
||||||
|
# cycle's answer stands without it. A tamid probe does not consult
|
||||||
|
# (the fire is checked, the engine is not loaded by the check).
|
||||||
|
defp consult_engine(%{probe: true} = data, _context), do: data
|
||||||
|
|
||||||
|
defp consult_engine(data, _context) do
|
||||||
|
roots = Map.get(data, :roots_activated, [])
|
||||||
|
names = roots |> Enum.map(&engine_root_name/1) |> Enum.reject(&is_nil/1)
|
||||||
|
case_ref = Map.get(data, :case_ref, generate_case_ref())
|
||||||
|
|
||||||
|
cond do
|
||||||
|
names == [] ->
|
||||||
|
Map.put(data, :talmudic_consulted, {:skipped, :no_roots})
|
||||||
|
|
||||||
|
Process.whereis(TalmudicEngineSocket) == nil ->
|
||||||
|
Map.put(data, :talmudic_consulted, {:error, :engine_socket_not_started})
|
||||||
|
|
||||||
|
true ->
|
||||||
|
case TalmudicEngineSocket.interpret(case_ref, %{context_roots: names, question: Map.get(data, :raw, "")}) do
|
||||||
|
{:ok, %{"status" => _} = reply} ->
|
||||||
|
Map.merge(data, %{talmudic_interpretation: reply, talmudic_consulted: :ok})
|
||||||
|
|
||||||
|
{:ok, other} ->
|
||||||
|
Logger.warning("BereishitCycle: engine answered an unexpected shape for #{case_ref}: #{inspect(other)}")
|
||||||
|
Map.put(data, :talmudic_consulted, {:error, :unexpected_reply})
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
Logger.warning("BereishitCycle: engine not consulted for #{case_ref}: #{inspect(reason)}")
|
||||||
|
Map.put(data, :talmudic_consulted, {:error, reason})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
The engine's reading, said plainly: how many verses and mitzvot it
|
||||||
|
coupled to the roots, and which. `nil` when there is no reading.
|
||||||
|
"""
|
||||||
|
@spec engine_reading_text(map()) :: String.t() | nil
|
||||||
|
def engine_reading_text(data) do
|
||||||
|
case Map.get(data, :talmudic_interpretation) do
|
||||||
|
%{"interpretation" => interp} = reply when is_map(interp) ->
|
||||||
|
verses = interp |> Map.get("verses", []) |> List.wrap() |> Enum.map(&verse_ref_display/1) |> Enum.reject(&is_nil/1) |> Enum.uniq()
|
||||||
|
mitzvot = interp |> Map.get("grounding", []) |> List.wrap() |> Enum.map(&mitzvah_display/1) |> Enum.reject(&is_nil/1) |> Enum.uniq()
|
||||||
|
status = Map.get(reply, "status", "")
|
||||||
|
|
||||||
|
cond do
|
||||||
|
verses == [] and mitzvot == [] ->
|
||||||
|
"Talmudic Engine (#{status}): no verse or mitzvah is coupled to these roots."
|
||||||
|
|
||||||
|
true ->
|
||||||
|
v = if verses == [], do: "no verse", else: "#{length(verses)} verse(s): #{Enum.join(verses, ", ")}"
|
||||||
|
m = if mitzvot == [], do: "no mitzvah", else: "#{length(mitzvot)} mitzvah/mitzvot: #{Enum.join(mitzvot, ", ")}"
|
||||||
|
"Talmudic Engine (#{status}): #{v}; #{m}."
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# "verse_ref(bereishit,1,1,'...')" / "verse(shemot,3,14)" → "Bereishit 1:1"
|
||||||
|
defp verse_ref_display(s) do
|
||||||
|
case Regex.run(~r/verse(?:_ref)?\((bereishit|shemot|vayikra|bamidbar|devarim)\s*,\s*(\d+)\s*,\s*(\d+)/, to_string(s)) do
|
||||||
|
[_, book, ch, vs] -> "#{String.capitalize(book)} #{ch}:#{vs}"
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# "mitzvah(1,positive,know_hashem,always)" → "know_hashem (1, positive)"
|
||||||
|
defp mitzvah_display(s) do
|
||||||
|
case Regex.run(~r/mitzvah\((\d+)\s*,\s*(\w+)\s*,\s*(\w+)/, to_string(s)) do
|
||||||
|
[_, id, kind, name] -> "#{name} (#{id}, #{kind})"
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Compose — the answer and its sources, from the context
|
# Compose — the answer and its sources, from the context
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -385,15 +473,19 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
deliberation =
|
deliberation =
|
||||||
case Map.get(data, :resolution) do
|
case Map.get(data, :resolution) do
|
||||||
:talmudic ->
|
:talmudic ->
|
||||||
text = get_in(data, [:talmudic_interpretation, "interpretation", "text"]) || ""
|
" " <> (engine_reading_text(data) || "Talmudic Engine resolved the case.")
|
||||||
" Talmudic Engine: #{text}"
|
|
||||||
|
|
||||||
:sanhedrin ->
|
:sanhedrin ->
|
||||||
pos = get_in(data, [:sanhedrin_decision, :majority, :position]) || ""
|
pos = get_in(data, [:sanhedrin_decision, :majority, :position]) || ""
|
||||||
" Sanhedrin majority: #{pos}"
|
" Sanhedrin majority: #{pos}"
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
""
|
# Consulted after ki_tov (every resolved question), or not.
|
||||||
|
case {engine_reading_text(data), Map.get(data, :talmudic_consulted)} do
|
||||||
|
{text, _} when is_binary(text) -> " " <> text
|
||||||
|
{nil, {:error, reason}} -> " Talmudic Engine: not consulted (#{inspect(reason)})."
|
||||||
|
_ -> ""
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
"Roots activated in #{verse.ref}: #{Enum.join(root_lines, "; ")}." <>
|
"Roots activated in #{verse.ref}: #{Enum.join(root_lines, "; ")}." <>
|
||||||
@@ -426,10 +518,19 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
text: verse.text_hebrew
|
text: verse.text_hebrew
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The engine's verses as references a reader can follow ("Bereishit
|
||||||
|
# 1:3"), the engine's own form kept beside them; the scope verse is
|
||||||
|
# not repeated.
|
||||||
talmudic_verses =
|
talmudic_verses =
|
||||||
case get_in(data, [:talmudic_interpretation, "interpretation", "verses"]) do
|
case get_in(data, [:talmudic_interpretation, "interpretation", "verses"]) do
|
||||||
vs when is_list(vs) -> Enum.map(vs, fn v -> %{ref: to_string(v)} end)
|
vs when is_list(vs) ->
|
||||||
_ -> []
|
vs
|
||||||
|
|> Enum.map(fn v -> %{ref: verse_ref_display(v) || to_string(v), engine_form: to_string(v)} end)
|
||||||
|
|> Enum.uniq_by(& &1.ref)
|
||||||
|
|> Enum.reject(&(&1.ref == verse.ref))
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
[primary | talmudic_verses]
|
[primary | talmudic_verses]
|
||||||
@@ -604,13 +705,22 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
{:interpretation, attrs, nil}
|
{:interpretation, attrs, nil}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
# The engine's reading, when it was consulted, rides in the record:
|
||||||
|
# its verses beside the scope, its text in the reasoning, and the
|
||||||
|
# component names both readers.
|
||||||
|
interp = get_in(data, [:talmudic_interpretation, "interpretation"]) || %{}
|
||||||
|
engine_verses = interp |> Map.get("verses", []) |> to_string_list()
|
||||||
|
reading = engine_reading_text(data)
|
||||||
|
|
||||||
attrs =
|
attrs =
|
||||||
Map.merge(base, %{
|
Map.merge(base, %{
|
||||||
reasoning: single_judge_reasoning(data, context),
|
reasoning:
|
||||||
|
if(reading, do: single_judge_reasoning(data, context) <> " " <> reading, else: single_judge_reasoning(data, context)),
|
||||||
|
torah_references: Enum.uniq([scope_ref | engine_verses]),
|
||||||
panel_size: 1,
|
panel_size: 1,
|
||||||
vote_count_for: 1,
|
vote_count_for: 1,
|
||||||
vote_count_against: 0,
|
vote_count_against: 0,
|
||||||
processing_component: "bereishit_cycle"
|
processing_component: if(reading, do: "bereishit_cycle+talmudic_engine", else: "bereishit_cycle")
|
||||||
})
|
})
|
||||||
|
|
||||||
{:interpretation, attrs, nil}
|
{:interpretation, attrs, nil}
|
||||||
@@ -740,7 +850,10 @@ defmodule VavPipeline.BereishitCycle do
|
|||||||
@doc false
|
@doc false
|
||||||
defp route_if_unresolved(data, context) do
|
defp route_if_unresolved(data, context) do
|
||||||
roots = Map.get(data, :roots_activated, [])
|
roots = Map.get(data, :roots_activated, [])
|
||||||
context_roots = Enum.map(roots, & &1.shoresh)
|
# Tablet 55: the engine names roots by letter (bet_resh_aleph); the
|
||||||
|
# Hebrew was sent since Step 14 and never matched a verse fact, so
|
||||||
|
# every Talmudic interpretation was "resolved" over an empty trace.
|
||||||
|
context_roots = roots |> Enum.map(&engine_root_name/1) |> Enum.reject(&is_nil/1)
|
||||||
|
|
||||||
case_ref = Map.get(data, :case_ref, generate_case_ref())
|
case_ref = Map.get(data, :case_ref, generate_case_ref())
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user