gate: Shorashim between a caller and a model, with the fringe and rebuke once (bishe'arecha step 4)
Devarim 6:9 / 11:20 — on your gates. POST /gate/anthropic/v1/messages and POST /gate/openai/v1/chat/completions take the model's own request shape under the caller's own upstream key (x-api-key, or Authorization with the courtyard key in X-Shorashim-Key), forward it unchanged (OTP inets/ssl, verify_peer; the Korban socket is serial, so the upstream call is made in the Vav Pipeline and the reply enters as korban like every bedikah), examine the reply with the request in hand — sources = the system prompt and the user turns, query = the last user turn; the model's prior turns are not sources — and deliver it with its fringe (Bamidbar 15:39): a trailer on the last text block naming ref, verdict, the rules with their verses and sentences; X-Shorashim-Bedikah / X-Shorashim-Verdict; a `shorashim` object. A non-200 passes through as it came; a reply with no text is not delivered; stream is refused. Rebuke once (Vayikra 19:17; X-Shorashim-Rebuke: once): a lo_tov reply is answered once more with the finding; the second reply is examined like the first, its record carries rebukes: <first ref> (migration 20261007000002), and it is delivered marked with both. Never a third. Manna: one api_calls_external per upstream call, gathered before any call. Stateless: no conversation, no key kept; the log is the path class. Gate.bearer accepts X-Shorashim-Key. Construction §3.10, Tamid §1.1, KOHEN "Standing at the gate", bishearecha §I/§III/§VII marked built. Elixir 592/0 (+14 gate tests incl. a real httpc round trip to a local model double), L3 9/0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
47025fbfeb
commit
5c63273dba
16 files changed
+1082
-15
No files matched your search
@@ -0,0 +1,137 @@
|
||||
defmodule VavPipeline.Gate do
|
||||
@moduledoc """
|
||||
The gate — Shorashim between a caller and a model (Construction §3.10;
|
||||
bishe'arecha §I "on your gates", §III the fringe, §VII rebuke).
|
||||
|
||||
One pass: forward the caller's request unchanged under the caller's
|
||||
key; read the reply as korban with the request's own context in hand
|
||||
(sources = what the caller gave the model); deliver the reply with its
|
||||
fringe. With `rebuke: :once` and a `lo_tov` first reply, one more
|
||||
request carrying the finding, and the second reply — examined like
|
||||
the first — is the one delivered, marked with both.
|
||||
|
||||
Stateless. No conversation kept, no key kept.
|
||||
"""
|
||||
|
||||
alias VavPipeline.{Anchors, Bedikah}
|
||||
alias VavPipeline.Gate.{Shape, Upstream}
|
||||
|
||||
@type pass :: %{
|
||||
status: non_neg_integer(),
|
||||
reply: map(),
|
||||
bedikah: map() | nil,
|
||||
first: map() | nil,
|
||||
shorashim: map() | nil
|
||||
}
|
||||
|
||||
@doc """
|
||||
Options: `:rebuke` (`:once` | `:none`), `:extra_headers` (forwarded),
|
||||
`:forward` (fun/4, for tests), and the `Bedikah.examine/4` options.
|
||||
"""
|
||||
@spec pass(:anthropic | :openai, map(), String.t(), keyword()) ::
|
||||
{:ok, pass()} | {:waiting, atom()} | {:error, atom(), String.t()}
|
||||
def pass(upstream, body, key, opts \\ []) do
|
||||
forward = Keyword.get(opts, :forward, &Upstream.forward/4)
|
||||
extra = Keyword.get(opts, :extra_headers, [])
|
||||
rebuke = Keyword.get(opts, :rebuke, :none)
|
||||
examine_opts = Keyword.drop(opts, [:forward, :extra_headers, :rebuke])
|
||||
|
||||
with :ok <- gather_call(),
|
||||
{:ok, 200, reply} <- forward.(upstream, body, key, extra),
|
||||
{:ok, first} <- examine(upstream, body, reply, examine_opts) do
|
||||
case {rebuke, first.verdict} do
|
||||
{:once, :lo_tov} ->
|
||||
anchors = Anchors.for_violations(first.violations)
|
||||
first_text = Shape.reply_text(upstream, reply)
|
||||
body2 = Shape.with_rebuke(upstream, body, first_text, Shape.rebuke_text(first, anchors))
|
||||
|
||||
with :ok <- gather_call(),
|
||||
{:ok, 200, reply2} <- forward.(upstream, body2, key, extra),
|
||||
{:ok, second} <- examine(upstream, body2, reply2, Keyword.put(examine_opts, :rebukes, first.bedikah_ref)) do
|
||||
{:ok, deliver(upstream, reply2, second, first)}
|
||||
else
|
||||
{:ok, status, other} -> {:ok, %{status: status, reply: other, bedikah: nil, first: first, shorashim: nil}}
|
||||
other -> other
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:ok, deliver(upstream, reply, first, nil)}
|
||||
end
|
||||
else
|
||||
# A non-200 from the upstream passes through as it came.
|
||||
{:ok, status, reply} -> {:ok, %{status: status, reply: reply, bedikah: nil, first: nil, shorashim: nil}}
|
||||
{:error, reason} when is_binary(reason) -> {:error, :upstream_unavailable, reason}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
# One api_calls_external per upstream call (Vayakhel §3.7). The
|
||||
# compute unit for the examination is gathered by Bedikah.examine.
|
||||
defp gather_call do
|
||||
if Process.whereis(VavPipeline.MannaLedger) do
|
||||
case VavPipeline.MannaLedger.load_provision() do
|
||||
{:ok, :no_provision} ->
|
||||
{:waiting, :no_provision}
|
||||
|
||||
{:error, _} ->
|
||||
{:waiting, :no_provision}
|
||||
|
||||
{:ok, _} ->
|
||||
case VavPipeline.MannaLedger.consume(:api_calls_external, 1) do
|
||||
{:ok, _} -> :ok
|
||||
{:error, :provision_exhausted} -> {:waiting, :api_calls_exhausted}
|
||||
{:error, :no_provision} -> {:waiting, :no_provision}
|
||||
end
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp examine(upstream, body, reply, opts) do
|
||||
case Shape.reply_text(upstream, reply) do
|
||||
nil ->
|
||||
{:error, :upstream_unavailable, "the reply from #{upstream} carries no text to examine"}
|
||||
|
||||
text ->
|
||||
provenance = Shape.provenance(upstream)
|
||||
query = Shape.query(upstream, body)
|
||||
sources = Shape.sources(upstream, body)
|
||||
|
||||
case Bedikah.examine(text, provenance, query, Keyword.put(opts, :sources, sources)) do
|
||||
{:ok, result} -> {:ok, result}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp deliver(upstream, reply, result, first) do
|
||||
anchors = Anchors.for_violations(result.violations)
|
||||
trailer = Shape.trailer(result, anchors, first)
|
||||
|
||||
shorashim =
|
||||
%{
|
||||
bedikah_ref: result.bedikah_ref,
|
||||
verdict: Atom.to_string(result.verdict),
|
||||
violations: result.violations,
|
||||
anchors: anchors,
|
||||
fired: Shape.fired(result, anchors),
|
||||
checks: result.checks,
|
||||
sources: result.sources,
|
||||
model: Shape.reply_model(upstream, reply)
|
||||
}
|
||||
|> then(fn m ->
|
||||
if first,
|
||||
do: Map.put(m, :rebuked, %{first_bedikah_ref: first.bedikah_ref, first_verdict: Atom.to_string(first.verdict)}),
|
||||
else: m
|
||||
end)
|
||||
|
||||
%{
|
||||
status: 200,
|
||||
reply: Shape.with_fringe(upstream, reply, trailer),
|
||||
bedikah: result,
|
||||
first: first,
|
||||
shorashim: shorashim
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user