Step 14: Connect components — chamishim karsay zahav
Three connections built and tested: - Haskell Shoresh Engine via Erlang Port (JSON lines over stdin/stdout) - Prolog Talmudic Engine via Unix domain socket (JSON over /tmp/shorashim/talmudic.sock) - Erlang Sanhedrin via native BEAM calls New: Server.hs (JSON-line server loop), json_bridge.pl + socket_server.pl (Prolog socket interface), ShoreshEnginePort + TalmudicEngineSocket + SanhedrinClient (Elixir GenServer wrappers). BereishitCycle now routes unresolved cases through Talmudic Engine and Sanhedrin. All connections gracefully degrade — pipeline falls back to L1 data when engines unavailable. 46 Elixir tests, 33 Haskell tests, 57 Prolog tests — 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f8f70c77e5
commit
c67672777b
18 files changed
+1435
-121
No files matched your search
@@ -19,7 +19,8 @@ defmodule VavPipeline.BereishitCycle do
|
||||
Latency budget (Construction spec §2.5): 5000ms total
|
||||
"""
|
||||
|
||||
alias VavPipeline.{Specification, CycleLog, Manna}
|
||||
alias VavPipeline.{Specification, CycleLog, Manna,
|
||||
ShoreshEngineClient, TalmudicEngineClient, SanhedrinClient}
|
||||
|
||||
require Logger
|
||||
|
||||
@@ -81,27 +82,22 @@ defmodule VavPipeline.BereishitCycle do
|
||||
"""
|
||||
@spec vayomer(map(), VavPipeline.Shema.shema_context()) :: map()
|
||||
def vayomer(state, context) do
|
||||
# In Phase 2: use L1 data from ShemaContext directly
|
||||
# In Phase 3: call ShoreshEngineClient.process(state.raw, context)
|
||||
roots =
|
||||
context.verse.words
|
||||
|> Enum.filter(fn w -> w.shoresh != nil end)
|
||||
|> Enum.map(fn w ->
|
||||
%{
|
||||
shoresh: w.shoresh,
|
||||
word: w.consonants,
|
||||
position: w.position,
|
||||
morph: w.morph,
|
||||
gematria: Map.get(context.root_gematria, w.shoresh, 0)
|
||||
}
|
||||
end)
|
||||
# Step 14: Call Shoresh Engine via port. Falls back to L1 data.
|
||||
case ShoreshEngineClient.process(state.raw, context) do
|
||||
{:ok, engine_result} ->
|
||||
Map.merge(state, %{
|
||||
roots_activated: get_result(engine_result, :roots_activated, "roots_activated", []),
|
||||
parse_tree: get_result(engine_result, :parse_tree, "parse_tree", nil),
|
||||
dual_readings: get_result(engine_result, :dual_readings, "dual_readings", []),
|
||||
teamim_parse: context.teamim_marks,
|
||||
ketiv_qere: context.ketiv_qere,
|
||||
stage: :vayomer
|
||||
})
|
||||
|
||||
Map.merge(state, %{
|
||||
roots_activated: roots,
|
||||
teamim_parse: context.teamim_marks,
|
||||
ketiv_qere: context.ketiv_qere,
|
||||
stage: :vayomer
|
||||
})
|
||||
{:error, reason} ->
|
||||
Logger.warning("BereishitCycle.vayomer: engine error (#{reason}), using L1 fallback")
|
||||
vayomer_fallback(state, context)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -202,7 +198,7 @@ defmodule VavPipeline.BereishitCycle do
|
||||
"""
|
||||
@spec erev_voker(VavPipeline.cycle_result(), VavPipeline.Shema.shema_context()) ::
|
||||
VavPipeline.cycle_result()
|
||||
def erev_voker(result, _context) do
|
||||
def erev_voker(result, context) do
|
||||
# Log the cycle
|
||||
phase =
|
||||
case result do
|
||||
@@ -217,11 +213,133 @@ defmodule VavPipeline.BereishitCycle do
|
||||
# Enforce anti-hoarding
|
||||
Manna.enforce_anti_hoarding()
|
||||
|
||||
result
|
||||
# Step 14: Route unresolved cases to Talmudic Engine / Sanhedrin
|
||||
case result do
|
||||
{:unresolved, data} ->
|
||||
route_if_unresolved(data, context)
|
||||
|
||||
_ ->
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
# --- Routing ---
|
||||
|
||||
@doc false
|
||||
defp route_if_unresolved(data, context) do
|
||||
# Extract roots for the Talmudic Engine query
|
||||
roots = Map.get(data, :roots_activated, [])
|
||||
context_roots = Enum.map(roots, fn
|
||||
%{shoresh: s} when is_binary(s) -> s
|
||||
%{"shoresh" => s} when is_list(s) -> Enum.join(s, "-")
|
||||
_ -> "unknown"
|
||||
end)
|
||||
|
||||
case_ref = "cycle-#{:erlang.system_time(:millisecond)}"
|
||||
|
||||
talmudic_context = %{
|
||||
context_roots: context_roots,
|
||||
question: "Interpret roots #{inspect(context_roots)} in context"
|
||||
}
|
||||
|
||||
# First: consult the Talmudic Engine
|
||||
case TalmudicEngineClient.interpret(case_ref, talmudic_context) do
|
||||
{:ok, %{"status" => "resolved"} = interpretation} ->
|
||||
Logger.info("BereishitCycle: Talmudic Engine resolved case #{case_ref}")
|
||||
{:ok, Map.merge(data, %{
|
||||
talmudic_interpretation: interpretation,
|
||||
resolution: :talmudic,
|
||||
stage: :erev_voker
|
||||
})}
|
||||
|
||||
{:ok, %{"status" => "teiku"}} ->
|
||||
# Still unresolved — escalate to Sanhedrin
|
||||
Logger.info("BereishitCycle: Talmudic Engine returned teiku, escalating to Sanhedrin")
|
||||
escalate_to_sanhedrin(data, context, case_ref, context_roots)
|
||||
|
||||
{:ok, interpretation} ->
|
||||
# Challenged or other status — include and return
|
||||
{:unresolved, Map.merge(data, %{
|
||||
talmudic_interpretation: interpretation,
|
||||
stage: :erev_voker
|
||||
})}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("BereishitCycle: Talmudic routing failed: #{reason}")
|
||||
{:unresolved, Map.put(data, :routing_error, reason)}
|
||||
end
|
||||
end
|
||||
|
||||
defp escalate_to_sanhedrin(data, _context, case_ref, context_roots) do
|
||||
sanhedrin_params = %{
|
||||
case_ref: case_ref,
|
||||
complexity: :simple,
|
||||
question: "Deliberate on unresolved interpretation of #{inspect(context_roots)}",
|
||||
context: %{
|
||||
roots: context_roots,
|
||||
named_results: Map.get(data, :named_results, [])
|
||||
},
|
||||
requested_by: "bereishit_cycle"
|
||||
}
|
||||
|
||||
case SanhedrinClient.submit_case(sanhedrin_params) do
|
||||
{:ok, decision} ->
|
||||
outcome = Map.get(decision, :outcome, :teiku)
|
||||
Logger.info("BereishitCycle: Sanhedrin decided #{outcome} for #{case_ref}")
|
||||
|
||||
case outcome do
|
||||
:accepted ->
|
||||
{:ok, Map.merge(data, %{
|
||||
sanhedrin_decision: decision,
|
||||
resolution: :sanhedrin,
|
||||
stage: :erev_voker
|
||||
})}
|
||||
|
||||
_ ->
|
||||
{:unresolved, Map.merge(data, %{
|
||||
sanhedrin_decision: decision,
|
||||
stage: :erev_voker
|
||||
})}
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("BereishitCycle: Sanhedrin escalation failed: #{reason}")
|
||||
{:unresolved, Map.put(data, :escalation_error, reason)}
|
||||
end
|
||||
end
|
||||
|
||||
# --- L1 Fallback (when port is unavailable) ---
|
||||
|
||||
defp vayomer_fallback(state, context) do
|
||||
roots =
|
||||
context.verse.words
|
||||
|> Enum.filter(fn w -> w.shoresh != nil end)
|
||||
|> Enum.map(fn w ->
|
||||
%{
|
||||
shoresh: w.shoresh,
|
||||
word: w.consonants,
|
||||
position: w.position,
|
||||
morph: w.morph,
|
||||
gematria: Map.get(context.root_gematria, w.shoresh, 0)
|
||||
}
|
||||
end)
|
||||
|
||||
Map.merge(state, %{
|
||||
roots_activated: roots,
|
||||
teamim_parse: context.teamim_marks,
|
||||
ketiv_qere: context.ketiv_qere,
|
||||
stage: :vayomer
|
||||
})
|
||||
end
|
||||
|
||||
# --- Private helpers ---
|
||||
|
||||
# Get a value from a map that may have atom or string keys
|
||||
# (atom keys from L1 fallback, string keys from JSON engine response)
|
||||
defp get_result(map, atom_key, string_key, default) do
|
||||
Map.get(map, atom_key, Map.get(map, string_key, default))
|
||||
end
|
||||
|
||||
defp extract_content(input) do
|
||||
Map.get(input, :content, "") |> String.trim()
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user