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>
106 lines
2.7 KiB
Elixir
106 lines
2.7 KiB
Elixir
defmodule VavPipeline.TalmudicEngineClient do
|
|
@moduledoc """
|
|
Client interface to the SWI-Prolog Talmudic Engine.
|
|
|
|
Construction spec §3.3:
|
|
Protocol: JSON over Unix domain socket
|
|
|
|
Delegates to TalmudicEngineSocket (the live Prolog process).
|
|
Falls back to stub responses if socket is unavailable.
|
|
"""
|
|
|
|
require Logger
|
|
|
|
@doc """
|
|
Send an interpretation request to the Talmudic Engine.
|
|
"""
|
|
@spec interpret(String.t(), map()) :: {:ok, map()} | {:error, String.t()}
|
|
def interpret(case_ref, context) do
|
|
case call_socket(:interpret, case_ref, context) do
|
|
{:ok, result} ->
|
|
{:ok, result}
|
|
|
|
{:error, reason} ->
|
|
Logger.warning("TalmudicEngineClient: socket unavailable (#{reason}), using stub")
|
|
{:ok, stub_interpretation(case_ref)}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Send a challenge request.
|
|
"""
|
|
@spec challenge(String.t(), map()) :: {:ok, map()} | {:error, String.t()}
|
|
def challenge(case_ref, context) do
|
|
case call_socket(:challenge, case_ref, context) do
|
|
{:ok, result} ->
|
|
{:ok, result}
|
|
|
|
{:error, reason} ->
|
|
Logger.warning("TalmudicEngineClient: socket unavailable (#{reason}), using stub")
|
|
{:ok, stub_challenge(case_ref)}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Send a resolution request.
|
|
"""
|
|
@spec resolve(String.t(), map()) :: {:ok, map()} | {:error, String.t()}
|
|
def resolve(case_ref, context) do
|
|
case call_socket(:resolve, case_ref, context) do
|
|
{:ok, result} ->
|
|
{:ok, result}
|
|
|
|
{:error, reason} ->
|
|
Logger.warning("TalmudicEngineClient: socket unavailable (#{reason}), using stub")
|
|
{:ok, stub_resolution(case_ref)}
|
|
end
|
|
end
|
|
|
|
# --- Private ---
|
|
|
|
defp call_socket(operation, case_ref, context) do
|
|
if Process.whereis(VavPipeline.TalmudicEngineSocket) do
|
|
apply(VavPipeline.TalmudicEngineSocket, operation, [case_ref, context])
|
|
else
|
|
{:error, "socket not started"}
|
|
end
|
|
end
|
|
|
|
defp stub_interpretation(case_ref) do
|
|
%{
|
|
"status" => "teiku",
|
|
"interpretation" => %{
|
|
"text" => "Talmudic Engine not connected — teiku",
|
|
"prolog_form" => "stub(#{case_ref})",
|
|
"grounding" => [],
|
|
"confidence" => "low"
|
|
},
|
|
"challenge" => nil
|
|
}
|
|
end
|
|
|
|
defp stub_challenge(_case_ref) do
|
|
%{
|
|
"status" => "teiku",
|
|
"challenge" => %{
|
|
"kushya" => "No challenge generated — engine not connected",
|
|
"contradicting" => "",
|
|
"strength" => "weak"
|
|
}
|
|
}
|
|
end
|
|
|
|
defp stub_resolution(case_ref) do
|
|
%{
|
|
"status" => "teiku",
|
|
"interpretation" => %{
|
|
"text" => "Resolution deferred — engine not connected",
|
|
"prolog_form" => "stub(#{case_ref})",
|
|
"grounding" => [],
|
|
"confidence" => "low"
|
|
},
|
|
"challenge" => nil
|
|
}
|
|
end
|
|
end
|