feat(response_adapter): close PARTIAL gaps — dichotomy wave

audit 5/7 PARTIAL -> 7/7 FULL (RUST side). Ported name-preserving:
to_dict (ordered dict form — serde struct field order = dataclass field
order, serde_json preserve_order keeps provenance/debug_refs insertion
order), parse_plain_response + parse_response as REAL fns (the audit's
substring match over-credited a test name and a doc mention — the
dispatcher's auto-detect and the plain parser did not exist), and
test_adapter assert-parity. REAL parity bugs fixed on the existing
parsers: parse_mimo_response now carries usage reasoning_tokens/
completion_tokens/total_tokens and parse_qwen_response
reasoning_words/completion_tokens/total_tokens with Python .get(k, 0)
pass-through semantics (ANY JSON value rides), parse_local_response
reasoning_tokens on the match branch; model/provider pass-through raw.

Truth: 36-case corpus (frozen_ra_gap_truth.json) via the shared
freeze_truth.py harness + scripts/ra_freeze_seams rig (pinned
RUNE_RESPONSE_ADAPTER_CORE=python; to_dict-carrying wrappers; CROSS-CALL
law — parse_response resolves its parser dict from module globals at
call time and test_adapter reads .user_text off the result, so the rig
restores originals around the self-test; no clock/HOME seams: the module
is pure). --verify byte-identical twice. Battery gap_battery 7/7 (36-case
VERBATIM order-sensitive replay + FFI roundtrip + key-order/auto-detect/
assert branch proofs). FFI: ra_parse_response + ra_replay cdylib entries
(cs_free_string serves the process); facade delegation in
src/rune/response_adapter.py + ra_loader.py with RUNE_RESPONSE_ADAPTER_
CORE=python pin (to_dict/test_adapter stay Python on the facade — pure
projection / stdout contract, W6 FFI-overhead law). Live parity 100%
(scripts/check_response_adapter_parity.py: FROZEN 36/36 ordered compare
through ra_replay, FUZZ 2000 ops seed 2479 incl. unicode/floats/nulls/
nested tags, PINS 10 facade calls identical under both pins); clippy -D
warnings + fmt + cargo build --release clean; contracts PASS + 7-fn
facade smoke + test_contracts_port.py green BOTH pins. Divergences
documented in verification/ra-summary.md (non-str content coercion,
non-dict usage, trim/strip control-char boundary).

Evidence: verification/ra-*.exit + ra-summary.md (logs local, *.log
gitignored).
This commit is contained in:
Nick committed 2026-10-14 16:40:25 +01:00
1 parent 653fbccf70
commit 1e3967a9f0
24 files changed
+2367 -109

No files matched your search

+6 -6
View File
@@ -2,16 +2,16 @@
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
**FULL completion: 62%** (120/192 runtime modules at FULL)
**FULL completion: 63%** (121/192 runtime modules at FULL)
Function-level detail: 74% (1690/2288 runtime functions matched) — secondary; includes in-flight work
Function-level detail: 74% (1692/2288 runtime functions matched) — secondary; includes in-flight work
| Status | Modules | Lines | Meaning |
|---|---|---|---|
| FULL | 120 | 49341 | 80%+ of functions ported |
| PARTIAL | 33 | 14691 | 20–80% — real logic exists, gaps named below |
| FULL | 121 | 49623 | 80%+ of functions ported |
| PARTIAL | 32 | 14469 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 39 | 11471 | no name-preserving implementation |
| Total runtime | 192 | 75503 | |
| Total runtime | 192 | 75563 | |
## NOT TOUCHED — the port queue, in priority order (largest first)
@@ -81,7 +81,6 @@ Function-level detail: 74% (1690/2288 runtime functions matched) — secondary;
| state_checkpoint | 182 | 6/9 | _sck_core, _load_ledger, _save_ledger |
| warm_neuron_store | 122 | 3/6 | _db, neuron_slugs, handle_hook |
| inference_trace | 204 | 4/6 | record_trace, read_traces |
| response_adapter | 222 | 5/7 | test_adapter, to_dict |
| structural_migration | 77 | 2/4 | apply_migration, consent_brief |
| subconscious_pass | 236 | 3/5 | main, get_recent_findings |
| subsystem_health | 213 | 4/6 | set_auto_filer, configure_logging |
@@ -177,6 +176,7 @@ Function-level detail: 74% (1690/2288 runtime functions matched) — secondary;
| toolchain | 295 | 5/6 | RUST |
| tiered_executor | 291 | 8/8 | GO |
| salience_gate | 283 | 6/6 | RUST |
| response_adapter | 282 | 7/7 | RUST |
| standardized_providers | 281 | 5/5 | GO |
| shard_manager | 276 | 9/9 | RUST |
| auto_context | 273 | 8/8 | GO |
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/env python3
"""Live parity: response_adapter Rust core vs Python oracle (dichotomy
wave, close-PARTIAL gap pass). Layers:
1. FROZEN — the gap truth corpus (src/rust/response_adapter/
frozen_ra_gap_truth.json, generated by
scripts/gen_response_adapter_truth.py against the LIVE Python oracle
through the scripts/ra_freeze_seams rig: pinned
RUNE_RESPONSE_ADAPTER_CORE=python floor, to_dict-carrying wrappers)
replayed through the ra_replay ctypes FFI entry of the built cdylib,
case by case, with ORDER-SENSITIVE comparison (json.dumps preserves
the insertion order on both sides — a key-order divergence fails).
2. FUZZ — seeded random responses (seed 2479): random content built
from token fragments including <think>/</think>, Thinking Process:/
Final:, unicode, whitespace runs; random usage dicts (ints, floats,
nulls), model/provider strings. The oracle's parse fns (pin ON, pure
Python) vs the Rust core through the FFI, exact ordered-dict compare
of the to_dict forms, across explicit and auto providers.
3. PINS — dual-mode facade battery: the same facade calls executed with
RUNE_RESPONSE_ADAPTER_CORE=python (oracle floor) and with the Rust
core live (delegation ON); results must be identical (the facade
both-pins law).
Exit 0 only at 100% parity.
"""
import json
import os
import random
import subprocess
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
CRATE = REPO / "src" / "rust" / "response_adapter"
FROZEN = CRATE / "frozen_ra_gap_truth.json"
os.environ["RUNE_RESPONSE_ADAPTER_CORE"] = "python" # oracle floor to start
sys.path.insert(0, str(REPO / "src"))
import rune.response_adapter as ra # noqa: E402
import rune.ra_loader as core # noqa: E402
if not core.available():
sys.exit(f"FATAL: Rust core not built — cargo build --release in {CRATE}")
TRUTH = json.loads(FROZEN.read_text())
fails: list[str] = []
counts = {"frozen": 0, "fuzz": 0, "pins": 0}
def fail(layer, name, detail):
fails.append(f"[{layer}] {name}: {detail}")
def ordered(obj) -> str:
return json.dumps(obj, ensure_ascii=False, separators=(", ", ": "))
def frozen_layer() -> None:
for i, case in enumerate(TRUTH):
try:
got = core.replay(case)
except Exception as e: # noqa: BLE001
fail("FROZEN", f"case{i}:{case['fn']}", f"FFI error {e}")
continue
want = case["expected"]
if ordered(got) != ordered(want):
fail("FROZEN", f"case{i}:{case['fn']}",
f"\n rust: {ordered(got)[:200]}\n want: {ordered(want)[:200]}")
else:
counts["frozen"] += 1
FRAGMENTS = [
"<think>", "</think>", "Thinking Process:", "Final:", "hello", "wörld",
"答案", "🜏", " ", "\n", "\t", "a b", "x", "", "Final", "<think >",
"process:", "think", "…", "\r\n", " three spaces ", "Final: ",
]
MODELS = ["mimo-v2.5-pro", "qwen3:14b", "", "モデル", None]
PROVIDERS = [None, "mimo", "qwen", "local", "plain", "weird"]
def rand_response(rng) -> dict:
content = "".join(rng.choice(FRAGMENTS)
for _ in range(rng.randint(0, 14)))
resp: dict = {"content": content}
m = rng.choice(MODELS)
if m is not None:
resp["model"] = m
if rng.random() < 0.5:
resp["reasoning_content"] = "".join(
rng.choice(FRAGMENTS) for _ in range(rng.randint(0, 6)))
if rng.random() < 0.5:
usage: dict = {}
for key in ("reasoning_tokens", "completion_tokens", "total_tokens"):
r = rng.random()
if r < 0.4:
usage[key] = rng.randint(0, 5000)
elif r < 0.5:
usage[key] = float(rng.randint(0, 90)) / 3
elif r < 0.55:
usage[key] = None
resp["usage"] = usage
if rng.random() < 0.3:
resp["provider"] = rng.choice(["openai", "groq", ""])
return resp
def fuzz_layer(n_seed: int = 400) -> None:
rng = random.Random(2479)
parse_fns = [
("parse_mimo_response", ra.parse_mimo_response,
core.parse_mimo_response),
("parse_qwen_response", ra.parse_qwen_response,
core.parse_qwen_response),
("parse_local_response", ra.parse_local_response,
core.parse_local_response),
("parse_plain_response", ra.parse_plain_response,
core.parse_plain_response),
]
for i in range(n_seed):
resp = rand_response(rng)
for name, py_fn, rs_fn in parse_fns:
want = py_fn(dict(resp)).to_dict()
try:
got = rs_fn(dict(resp))
except Exception as e: # noqa: BLE001
fail("FUZZ", f"{i}:{name}", f"FFI error {e}")
continue
if ordered(got) != ordered(want):
fail("FUZZ", f"{i}:{name}",
f"resp={json.dumps(resp, ensure_ascii=False)}\n"
f" rust: {ordered(got)[:220]}\n want: {ordered(want)[:220]}")
else:
counts["fuzz"] += 1
provider = rng.choice(PROVIDERS)
want = ra.parse_response(dict(resp), provider).to_dict()
try:
got = core.parse_response(dict(resp), provider)
except Exception as e: # noqa: BLE001
fail("FUZZ", f"{i}:parse_response", f"FFI error {e}")
continue
if ordered(got) != ordered(want):
fail("FUZZ", f"{i}:parse_response",
f"resp={json.dumps(resp, ensure_ascii=False)} provider={provider}\n"
f" rust: {ordered(got)[:220]}\n want: {ordered(want)[:220]}")
else:
counts["fuzz"] += 1
PIN_SNIPPET = r"""
import json, os, sys
sys.path.insert(0, "src")
import rune.response_adapter as ra
samples = [
{"reasoning_content": "think hard", "content": "mimo answer",
"model": "mimo-v2.5-pro",
"usage": {"reasoning_tokens": 4, "completion_tokens": 3, "total_tokens": 7}},
{"content": "<think>one two three</think>\n\n the answer ",
"model": "qwen3:14b", "usage": {"completion_tokens": 20}},
{"content": "Thinking Process: reason it out\nline two Final: final text",
"model": "qwen2.5:3b"},
{"content": "plain words", "provider": "openai", "model": "gpt-4"},
{"content": "auto <think>x</think> shape"},
{"content": "auto Thinking Process: y Final: z"},
]
out = [ra.parse_response(s).to_dict() for s in samples]
out += [ra.parse_mimo_response(samples[0]).to_dict(),
ra.parse_qwen_response(samples[1]).to_dict(),
ra.parse_local_response(samples[2]).to_dict(),
ra.parse_plain_response(samples[3]).to_dict()]
print(json.dumps(out, ensure_ascii=False))
"""
def pins_layer() -> None:
results = {}
for pin in ("python", "rust"):
env = dict(os.environ)
env.pop("RUNE_RESPONSE_ADAPTER_CORE", None)
if pin == "python":
env["RUNE_RESPONSE_ADAPTER_CORE"] = "python"
env["RA_RUST_LIB"] = str(CRATE / "target" / "release" /
"libresponse_adapter.so")
r = subprocess.run([sys.executable, "-c", PIN_SNIPPET],
cwd=str(REPO), env=env, capture_output=True,
text=True, timeout=120)
if r.returncode != 0:
fail("PINS", pin, f"snippet failed:\n{r.stderr[-400:]}")
return
results[pin] = r.stdout
if results["python"] != results["rust"]:
fail("PINS", "both-pins", "python-pinned vs live-core outputs differ")
else:
counts["pins"] = len(json.loads(results["python"]))
if __name__ == "__main__":
frozen_layer()
fuzz_layer()
pins_layer()
print(f"FROZEN: {counts['frozen']}/{len(TRUTH)} cases verbatim through ra_replay")
print(f"FUZZ: {counts['fuzz']} op comparisons (seed 2479)")
print(f"PINS: {counts['pins']} facade calls identical under both pins")
if fails:
print(f"\n{len(fails)} FAILURES (first 8):")
for f in fails[:8]:
print(" -", f)
sys.exit(1)
print("PARITY 100%")
+171
View File
@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""gen_response_adapter_truth.py — freeze the response_adapter gap truth
(to_dict / parse_mimo_response / parse_qwen_response /
parse_local_response / parse_plain_response / parse_response /
test_adapter) through the SHARED harness (scripts/freeze_truth.py — one
law).
The oracle fns return RuneResponse dataclasses — plain JSON cannot carry
them — so this generator runs under the freeze rig
(scripts/ra_freeze_seams/sitecustomize.py): pinned
RUNE_RESPONSE_ADAPTER_CORE=python floor + to_dict-carrying wrappers (the
parse rows freeze the parser's canonical dict form; to_dict rows ride the
harness's recv mechanism; test_adapter freezes its None return). No
clock/HOME seams — the module is pure (no-clock law holds by
construction).
Run (from the repo root):
PYTHONPATH=scripts/ra_freeze_seams:src \
python3 scripts/gen_response_adapter_truth.py
Verify (byte-identical, same rig):
PYTHONPATH=scripts/ra_freeze_seams:src \
python3 scripts/freeze_truth.py --verify \
src/rust/response_adapter/frozen_ra_gap_truth.json \
--module rune.response_adapter
Every ported function and branch is exercised:
parse_mimo_response full usage / no usage / partial usage keys /
empty reasoning (debug_refs []), missing model,
missing content, empty response
parse_qwen_response one think / two thinks (reasoning_words sum) /
no think / empty content / think-only / multiline
think (DOTALL) with padded visible / usage+model
missing
parse_local_response match / no match / two "Final:" (greedy group 2) /
multiline thinking / match without model /
whitespace-run sandwich
parse_plain_response provider+model / neither / provider only
parse_response auto-detect all four shapes / explicit provider
each / unknown provider (plain fallback) /
explicit plain over mimo shape
to_dict full 7-field receiver / minimal receiver
test_adapter None return
"""
import os
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "scripts"))
from freeze_truth import freeze, write # noqa: E402
import sitecustomize as rig # noqa: E402 (rig-bound: pinned core + to_dict seams)
OUT = Path(os.environ.get("RA_TRUTH_OUT")
or REPO / "src" / "rust" / "response_adapter" / "frozen_ra_gap_truth.json")
FULL_RECV = {
"cls": "RuneResponse",
"user_text": "visible answer",
"work_events": [{"kind": "work", "n": 1}],
"tool_events": [{"tool": "shell"}],
"status_updates": ["compiling"],
"warnings": ["deprecated call"],
"debug_refs": [{"type": "reasoning", "content": "because"}],
"provenance": {"provider": "mimo", "model": "mimo-v2.5-pro",
"reasoning_tokens": 10, "completion_tokens": 8,
"total_tokens": 18},
}
MIN_RECV = {
"cls": "RuneResponse",
"user_text": "bare",
"work_events": [],
"tool_events": [],
"status_updates": [],
"warnings": [],
"debug_refs": [],
"provenance": {},
}
cases = [
# ── parse_mimo_response ──────────────────────────────────────────────────
("parse_mimo_response",
({"reasoning_content": "Let me think about this...",
"content": "Hello! How can I help?",
"model": "mimo-v2.5-pro",
"usage": {"reasoning_tokens": 10, "completion_tokens": 8,
"total_tokens": 18}},), {}),
("parse_mimo_response",
({"reasoning_content": "step one",
"content": "answer", "model": "m"},), {}), # no usage → zeros
("parse_mimo_response",
({"content": "answer only", "model": "m"},), {}), # empty reasoning
("parse_mimo_response", ({"reasoning_content": "r", "content": "c"},), {}), # model missing
("parse_mimo_response",
({"reasoning_content": "r", "model": "m"},), {}), # content missing
("parse_mimo_response",
({"reasoning_content": "r", "content": "c", "model": "m",
"usage": {"reasoning_tokens": 7}},), {}), # partial usage
("parse_mimo_response", ({},), {}), # empty response
# ── parse_qwen_response ──────────────────────────────────────────────────
("parse_qwen_response",
({"content": "<think>plan the answer</think>The answer is 42.",
"model": "qwen3:14b",
"usage": {"completion_tokens": 20, "total_tokens": 31}},), {}),
("parse_qwen_response",
({"content": "<think>one two</think>mid<think>three</think>tail",
"model": "q"},), {}), # two thinks
("parse_qwen_response",
({"content": "no think blocks here", "model": "q"},), {}),
("parse_qwen_response", ({"content": "", "model": "q"},), {}),
("parse_qwen_response",
({"content": "<think>only thoughts</think>"},), {}), # think-only
("parse_qwen_response",
({"content": "<think>line1\nline2\ttabbed</think>\n\n padded visible \n",
"model": "qwen3"},), {}), # DOTALL + strip
("parse_qwen_response",
({"content": "<think>words here</think>visible"},), {}), # usage+model missing
# ── parse_local_response ────────────────────────────────────────────────
("parse_local_response",
({"content": "Thinking Process: I need to respond helpfully. Final: Hello!",
"model": "qwen2.5:3b"},), {}),
("parse_local_response",
({"content": "just a plain local answer", "model": "l"},), {}), # no match
("parse_local_response",
({"content": "Thinking Process: a Final: first Final: second"},), {}), # greedy g2
("parse_local_response",
({"content": "Thinking Process: multi\nline\nreasoning Final: multi\nline answer",
"model": "l"},), {}),
("parse_local_response",
({"content": "Thinking Process: spaced out Final: done"},), {}), # ws sandwich
("parse_local_response",
({"content": "prefix Thinking Process: mid text Final: tail end",
"model": "local-model"},), {}), # pattern mid-string
# ── parse_plain_response ────────────────────────────────────────────────
("parse_plain_response",
({"content": "Hello!", "provider": "openai", "model": "gpt-4"},), {}),
("parse_plain_response", ({"content": "Hello!"},), {}),
("parse_plain_response", ({"content": "hi", "provider": "groq"},), {}),
# ── parse_response: auto-detect ──────────────────────────────────────────
("parse_response",
({"reasoning_content": "r", "content": "c", "model": "m"},), {}),
("parse_response", ({"content": "x <think>y</think> z", "model": "q"},), {}),
("parse_response", ({"content": "Thinking Process: a Final: b"},), {}),
("parse_response", ({"content": "plain words", "model": "g"},), {}),
# ── parse_response: explicit provider (positional + kwargs forms) ────────
("parse_response",
({"reasoning_content": "r", "content": "c", "model": "m",
"usage": {"total_tokens": 5}}, "mimo"), {}),
("parse_response", ({"content": "<think>q</think>v", "model": "q"}, "qwen"), {}),
("parse_response",
({"content": "Thinking Process: t Final: f", "model": "l"},),
{"provider": "local"}),
("parse_response",
({"content": "hello", "provider": "mistral", "model": "m"},),
{"provider": "plain"}),
("parse_response", ({"content": "weird shape"}, "weird"), {}), # unknown → plain
("parse_response",
({"reasoning_content": "r", "content": "c"},), {"provider": "plain"}),
# ── to_dict (recv rows — post-call receiver snapshot rides expected) ────
{"fn": "RuneResponse.to_dict", "recv": FULL_RECV, "args": [], "kwargs": {}},
{"fn": "RuneResponse.to_dict", "recv": MIN_RECV, "args": [], "kwargs": {}},
# ── test_adapter (None return; the prints are stdout, not truth) ────────
("test_adapter", (), {}),
]
truth = freeze("rune.response_adapter", cases,
oracle_module="rune.response_adapter")
write(truth, OUT)
print(f"froze {len(truth)} cases -> {OUT}")
+82
View File
@@ -0,0 +1,82 @@
# Freeze rig for the response_adapter dichotomy gap-closing.
# Auto-imported on interpreter start (PYTHONPATH=scripts/ra_freeze_seams:src)
# by BOTH the truth generator (scripts/gen_response_adapter_truth.py) and
# the shared harness's --verify mode, so frozen truth is byte-reproducible
# in any process. Mirrors scripts/lc_freeze_seams (the closest sibling:
# a pure-function module whose fns return DATACLASSES — plain JSON cannot
# carry a dataclass instance, so the rig shadows each parse fn with a
# to_dict-carrying wrapper that records the parser's canonical dict form).
#
# * RUNE_RESPONSE_ADAPTER_CORE is pinned to "python" — the oracle runs its
# pure-Python floors end to end (the shared harness's _pin_oracle_cores
# pins the same gate; the explicit pin here covers re-imports that race
# the harness, the poisoned-truth law).
# * TO-DICT SEAMS: the 5 parse fns return RuneResponse instances; the rig
# shadows each with a wrapper that returns the REAL oracle fn's output
# in to_dict form. CROSS-CALL LAW: parse_response resolves its parsers
# dict from module globals at CALL time (the shadows), so its inner
# results are already dicts — the wrapper passes those through instead
# of re-wrapping. test_adapter reads .user_text off parse_response's
# result, so its wrapper temporarily restores ALL originals (a
# single-threaded freeze process), runs the real self-test, then
# re-installs the shadows.
# * No clock, no HOME, no file IO in this module's surface — no clock or
# home sandbox needed (the no-clock law holds: no fn reads time).
import os
import sys
os.environ["RUNE_RESPONSE_ADAPTER_CORE"] = "python"
# blanket pin (the loop law from lc_freeze_seams: only vars already in the
# env — plus the explicit pin above for this cone's own gate)
for _k in list(os.environ):
if _k.startswith("RUNE_") and _k.endswith("_CORE"):
os.environ[_k] = "python"
_SRC = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(
os.path.abspath(__file__)))), "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
import rune.response_adapter as ra # noqa: E402
# Originals captured BEFORE the seam bindings below shadow the module
# globals — the wrappers must call the captured originals, never the
# shadowed name.
_ORIG = {name: getattr(ra, name) for name in (
"parse_mimo_response", "parse_qwen_response",
"parse_local_response", "parse_plain_response", "parse_response")}
def _to_dict_carrying(name):
orig = _ORIG[name]
def wrapper(*args, **kwargs):
r = orig(*args, **kwargs)
return r.to_dict() if hasattr(r, "to_dict") else r
wrapper.__name__ = name
wrapper.__wrapped__ = orig
return wrapper
_SHADOWS = {name: _to_dict_carrying(name) for name in _ORIG}
_ORIG_TEST_ADAPTER = ra.test_adapter
def _test_adapter_wrapper():
saved = {n: getattr(ra, n) for n in _ORIG}
for n in _ORIG:
setattr(ra, n, _ORIG[n])
try:
return _ORIG_TEST_ADAPTER()
finally:
for n, w in saved.items():
setattr(ra, n, w)
for _name, _shadow in _SHADOWS.items():
setattr(ra, _name, _shadow)
ra.test_adapter = _test_adapter_wrapper
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust response_adapter core (dichotomy wave
gap-close). Mirrors lc_loader.py's contract:
FFI contract (to_dict-value law):
* ra_parse_response({"fn", "response", "provider"?}) -> parser to_dict form
* ra_replay({fn, args, kwargs, recv?}) -> frozen-truth replay value
Override the .so with RA_RUST_LIB (a FILE path).
"""
from __future__ import annotations
import ctypes
import json
import os
from pathlib import Path
_REPO = Path(__file__).resolve().parents[2]
_CRATE_DIR = _REPO / "src" / "rust" / "response_adapter"
_LIB_PATH = Path(os.environ.get("RA_RUST_LIB", _CRATE_DIR / "target" / "release" / "libresponse_adapter.so"))
_lib = None
_SYMBOLS = ("ra_parse_response", "ra_replay")
class ResponseAdapterUnavailable(FileNotFoundError):
"""The Rust response core is not built. Callers fall back to Python."""
def available() -> bool:
return _LIB_PATH.is_file()
def _load() -> ctypes.CDLL:
global _lib
if _lib is not None:
return _lib
if not _LIB_PATH.is_file():
raise ResponseAdapterUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in _SYMBOLS:
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
_lib = lib
return lib
def _call(sym: str, payload: dict):
lib = _load()
data = json.dumps(payload).encode()
buf = ctypes.create_string_buffer(data)
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
if not ptr:
raise RuntimeError(f"{sym} returned null")
out = ctypes.string_at(ptr).decode()
try:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
scoring.cs_free_string(ptr)
except OSError:
pass
doc = json.loads(out)
if not doc.get("ok"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def _parse(fn: str, response: dict, provider=None):
payload = {"fn": fn, "response": response}
if provider is not None:
payload["provider"] = provider
return _call("ra_parse_response", payload)
def parse_mimo_response(response: dict) -> dict:
return _parse("parse_mimo_response", response)
def parse_qwen_response(response: dict) -> dict:
return _parse("parse_qwen_response", response)
def parse_local_response(response: dict) -> dict:
return _parse("parse_local_response", response)
def parse_plain_response(response: dict) -> dict:
return _parse("parse_plain_response", response)
def parse_response(response: dict, provider: str | None = None) -> dict:
return _parse("parse_response", response, provider)
def replay(case: dict):
return _call("ra_replay", case)
+65 -5
View File
@@ -17,11 +17,27 @@ Usage:
"""
import json
import os
import re
import sys
from dataclasses import dataclass, field
def _rust_core():
"""The Rust response core when built (dichotomy wave gap-close).
RUNE_RESPONSE_ADAPTER_CORE=python pins the fallback."""
if os.environ.get("RUNE_RESPONSE_ADAPTER_CORE", "auto") == "python":
return None
try:
from rune import ra_loader
except Exception: # noqa: BLE001 — any loader failure means fallback
return None
try:
return ra_loader if ra_loader.available() else None
except Exception: # noqa: BLE001 — probe must never crash the fallback
return None
@dataclass
class RuneResponse:
"""Canonical response from any provider."""
@@ -47,7 +63,16 @@ class RuneResponse:
def parse_mimo_response(response: dict) -> RuneResponse:
"""Parse MiMo response with reasoning_content + content."""
"""Parse MiMo response with reasoning_content + content.
Dichotomy wave gap-close: delegates to the Rust kernel when built
(RUNE_RESPONSE_ADAPTER_CORE=python pins the Python oracle below)."""
core = _rust_core()
if core is not None:
try:
return RuneResponse(**core.parse_mimo_response(response))
except Exception: # noqa: BLE001 — kernel failure falls back
pass
reasoning = response.get("reasoning_content", "")
content = response.get("content", "")
@@ -69,7 +94,16 @@ def parse_mimo_response(response: dict) -> RuneResponse:
def parse_qwen_response(response: dict) -> RuneResponse:
"""Parse Qwen response with <think>...</think> blocks."""
"""Parse Qwen response with <think>...</think> blocks.
Dichotomy wave gap-close: delegates to the Rust kernel when built
(RUNE_RESPONSE_ADAPTER_CORE=python pins the Python oracle below)."""
core = _rust_core()
if core is not None:
try:
return RuneResponse(**core.parse_qwen_response(response))
except Exception: # noqa: BLE001 — kernel failure falls back
pass
content = response.get("content", "")
# Extract <think> blocks
@@ -97,7 +131,16 @@ def parse_qwen_response(response: dict) -> RuneResponse:
def parse_local_response(response: dict) -> RuneResponse:
"""Parse local text model response with Thinking Process / Final."""
"""Parse local text model response with Thinking Process / Final.
Dichotomy wave gap-close: delegates to the Rust kernel when built
(RUNE_RESPONSE_ADAPTER_CORE=python pins the Python oracle below)."""
core = _rust_core()
if core is not None:
try:
return RuneResponse(**core.parse_local_response(response))
except Exception: # noqa: BLE001 — kernel failure falls back
pass
content = response.get("content", "")
# Try to extract "Thinking Process: ... Final: ..." pattern
@@ -128,7 +171,16 @@ def parse_local_response(response: dict) -> RuneResponse:
def parse_plain_response(response: dict) -> RuneResponse:
"""Parse non-reasoning model response (plain content)."""
"""Parse non-reasoning model response (plain content).
Dichotomy wave gap-close: delegates to the Rust kernel when built
(RUNE_RESPONSE_ADAPTER_CORE=python pins the Python oracle below)."""
core = _rust_core()
if core is not None:
try:
return RuneResponse(**core.parse_plain_response(response))
except Exception: # noqa: BLE001 — kernel failure falls back
pass
content = response.get("content", "")
return RuneResponse(
@@ -144,7 +196,15 @@ def parse_response(response: dict, provider: str = None) -> RuneResponse:
"""Parse any provider response into canonical RuneResponse.
Auto-detects provider if not specified.
"""
Dichotomy wave gap-close: delegates to the Rust kernel when built
(RUNE_RESPONSE_ADAPTER_CORE=python pins the Python oracle below)."""
core = _rust_core()
if core is not None:
try:
return RuneResponse(**core.parse_response(response, provider))
except Exception: # noqa: BLE001 — kernel failure falls back
pass
if provider is None:
# Auto-detect from response structure
if "reasoning_content" in response:
+23
View File
@@ -11,6 +11,28 @@ dependencies = [
"memchr",
]
[[package]]
name = "equivalent"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "indexmap"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
@@ -115,6 +137,7 @@ version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"indexmap",
"itoa",
"memchr",
"serde",
+4 -1
View File
@@ -8,5 +8,8 @@ crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# preserve_order: to_dict and provenance follow the oracle's insertion
# order (the json insertion-order quirk); the default BTreeMap would
# silently sort those keys.
serde_json = { version = "1", features = ["preserve_order"] }
regex = "1"
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,237 @@
//! gap_battery — replay the FROZEN gap truth VERBATIM
//! (frozen_ra_gap_truth.json, generated by
//! scripts/gen_response_adapter_truth.py against the LIVE Python oracle
//! through the scripts/ra_freeze_seams rig: pinned
//! RUNE_RESPONSE_ADAPTER_CORE=python floor, to_dict-carrying wrappers).
//!
//! The battery never imports the Python oracle — parity stays provable
//! after the strangler exits. serde_json preserve_order keeps dict
//! insertion order stable, so comparisons are ORDER-SENSITIVE (serialized
//! forms must byte-match the frozen expected, which carries the oracle's
//! insertion order). Branch reachability beyond the frozen corpus is
//! proven directly (key order, auto-detect, unknown-provider dispatch,
//! assert-parity self-test).
use serde_json::{json, Value};
use std::ffi::{CStr, CString};
const TRUTH: &str = include_str!("../frozen_ra_gap_truth.json");
fn cases() -> Vec<Value> {
serde_json::from_str::<Value>(TRUTH)
.expect("frozen truth parses")
.as_array()
.cloned()
.expect("truth is a case list (legacy list format)")
}
fn case_id(c: &Value) -> String {
c.get("fn")
.and_then(Value::as_str)
.unwrap_or("?")
.to_string()
}
fn ordered(v: &Value) -> String {
serde_json::to_string(v).unwrap_or_default()
}
#[test]
fn frozen_gap_truth_replays_verbatim() {
let mut n = 0;
for case in cases() {
let got = crate::dispatch(&case).expect("case dispatches");
let want = case.get("expected").cloned().unwrap_or(Value::Null);
assert_eq!(
ordered(&got),
ordered(&want),
"case {} ({}) diverged",
case_id(&case),
n
);
n += 1;
}
assert!(n >= 30, "frozen corpus too thin: {n} cases");
}
#[test]
fn ra_replay_ffi_roundtrip() {
// the FFI surface returns the same dispatch computation the parity
// script drives through ra_loader
let case = &cases()[0];
let payload = serde_json::to_vec(case).expect("case serializes");
let cstr = CString::new(payload).expect("no NUL in JSON");
let ptr = crate::ra_replay(cstr.as_ptr());
let text = unsafe { CStr::from_ptr(ptr) }.to_str().expect("utf8");
let env: Value = serde_json::from_str(text).expect("envelope parses");
assert_eq!(env.get("ok"), Some(&json!(true)), "envelope ok: {text}");
assert_eq!(
ordered(env.get("value").unwrap_or(&Value::Null)),
ordered(&case.get("expected").cloned().unwrap_or(Value::Null)),
"ra_replay value must equal the frozen expected"
);
unsafe { drop(CString::from_raw(ptr)) };
}
#[test]
fn ra_parse_response_ffi_dispatches_all_parsers() {
let lib = [
(
"parse_mimo_response",
json!({"reasoning_content": "r", "content": "c", "usage": {"reasoning_tokens": 3}}),
),
(
"parse_qwen_response",
json!({"content": "<think>a b</think>ok"}),
),
(
"parse_local_response",
json!({"content": "Thinking Process: t Final: done"}),
),
(
"parse_plain_response",
json!({"content": "hi", "provider": "x", "model": "m"}),
),
];
for (fn_name, response) in lib {
let payload = json!({"fn": fn_name, "response": response});
let cstr =
CString::new(serde_json::to_vec(&payload).expect("payload serializes")).expect("utf8");
let ptr = crate::ra_parse_response(cstr.as_ptr());
let text = unsafe { CStr::from_ptr(ptr) }.to_str().expect("utf8");
let env: Value = serde_json::from_str(text).expect("envelope parses");
assert_eq!(env.get("ok"), Some(&json!(true)), "envelope ok: {text}");
let v = env.get("value").expect("value present");
assert_eq!(
v.get("provenance")
.and_then(|p| p.get("provider"))
.and_then(Value::as_str),
Some(match fn_name {
"parse_mimo_response" => "mimo",
"parse_qwen_response" => "qwen",
"parse_local_response" => "local",
_ => "x",
}),
"{fn_name} dispatch"
);
unsafe { drop(CString::from_raw(ptr)) };
}
}
#[test]
fn to_dict_emits_oracle_key_order() {
let r = crate::parse_mimo_response(&json!({"content": "x"}));
let d = r.to_dict();
let keys: Vec<&str> = d
.as_object()
.expect("object")
.keys()
.map(String::as_str)
.collect();
assert_eq!(
keys,
vec![
"user_text",
"work_events",
"tool_events",
"status_updates",
"warnings",
"debug_refs",
"provenance"
],
"to_dict key order must follow the dataclass field order"
);
}
#[test]
fn provenance_insertion_order_per_parser() {
let keys = |v: Value| -> Vec<String> {
v.get("provenance")
.and_then(|p| p.as_object())
.expect("provenance object")
.keys()
.cloned()
.collect()
};
assert_eq!(
keys(crate::parse_mimo_response(&json!({})).to_dict()),
vec![
"provider",
"model",
"reasoning_tokens",
"completion_tokens",
"total_tokens"
]
);
assert_eq!(
keys(crate::parse_qwen_response(&json!({})).to_dict()),
vec![
"provider",
"model",
"reasoning_words",
"completion_tokens",
"total_tokens"
]
);
assert_eq!(
keys(
crate::parse_local_response(&json!({"content": "Thinking Process: a Final: b"}))
.to_dict()
),
vec!["provider", "model", "reasoning_tokens"]
);
assert_eq!(
keys(crate::parse_local_response(&json!({"content": "nope"})).to_dict()),
vec!["provider", "model"]
);
assert_eq!(
keys(crate::parse_plain_response(&json!({})).to_dict()),
vec!["provider", "model"]
);
}
#[test]
fn parse_response_auto_detect_all_four() {
let auto = |resp: Value| -> String {
crate::parse_response(&resp, None)
.provenance
.get("provider")
.and_then(Value::as_str)
.expect("provider")
.to_string()
};
assert_eq!(
auto(json!({"reasoning_content": "r", "content": "c"})),
"mimo",
"reasoning_content key presence routes to mimo"
);
assert_eq!(auto(json!({"content": "x <think>y</think> z"})), "qwen");
assert_eq!(
auto(json!({"content": "Thinking Process: a Final: b"})),
"local"
);
assert_eq!(auto(json!({"content": "plain words"})), "unknown",
"plain route: parse_plain_response reads provider from the response (default unknown), not the route name");
// explicit provider overrides the shape; unknown provider → plain
assert_eq!(
crate::parse_response(&json!({"reasoning_content": "r"}), Some("plain"))
.provenance
.get("provider"),
Some(&json!("unknown")),
"plain parser's provider comes from response.get('provider', 'unknown')"
);
assert_eq!(
crate::parse_response(&json!({"content": "x"}), Some("weird"))
.provenance
.get("provider"),
Some(&json!("unknown")),
"unknown provider falls to the plain parser"
);
}
#[test]
fn test_adapter_assert_parity() {
// the self-test holds on the ported kernels (crash parity: an assert
// failure panics like the oracle's AssertionError)
assert_eq!(crate::test_adapter(), Value::Null);
}
+308 -97
View File
@@ -1,151 +1,362 @@
//! response_adapter — normalizes provider output (W6).
//! PARITY port of src/rune/response_adapter.py.
//! PARITY port of src/rune/response_adapter.py (dichotomy wave gap-close).
//!
//! serde_json preserve_order: to_dict and provenance emit the oracle's
//! insertion order (the json insertion-order quirk); the default BTreeMap
//! would silently sort those keys.
use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value};
use std::ffi::{c_char, CStr, CString};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RuneResponse {
#[serde(default)]
pub user_text: String,
#[serde(default)]
pub work_events: Vec<serde_json::Value>,
pub work_events: Vec<Value>,
#[serde(default)]
pub tool_events: Vec<serde_json::Value>,
pub tool_events: Vec<Value>,
#[serde(default)]
pub status_updates: Vec<serde_json::Value>,
pub status_updates: Vec<Value>,
#[serde(default)]
pub warnings: Vec<serde_json::Value>,
pub warnings: Vec<Value>,
#[serde(default)]
pub debug_refs: Vec<serde_json::Value>,
pub debug_refs: Vec<Value>,
#[serde(default)]
pub provenance: std::collections::BTreeMap<String, serde_json::Value>,
pub provenance: Map<String, Value>,
}
pub fn parse_mimo_response(response: &serde_json::Value) -> RuneResponse {
let content = response
.get("content")
.and_then(|c| c.as_str())
impl RuneResponse {
/// The canonical dict form — key order follows the dataclass field
/// order (serde emits struct fields in declaration order).
pub fn to_dict(&self) -> Value {
serde_json::to_value(self).expect("RuneResponse serializes")
}
}
fn str_field(response: &Value, key: &str) -> String {
response
.get(key)
.and_then(Value::as_str)
.unwrap_or("")
.to_string();
let reasoning = response
.get("reasoning_content")
.and_then(|r| r.as_str())
.unwrap_or("")
.to_string();
let model = response
.get("model")
.and_then(|m| m.as_str())
.unwrap_or("unknown")
.to_string();
let mut provenance = std::collections::BTreeMap::new();
provenance.insert("provider".to_string(), serde_json::json!("mimo"));
provenance.insert("model".to_string(), serde_json::json!(model));
.to_string()
}
/// Python `response.get(key, default)` pass-through: ANY JSON value rides
/// when the key exists (ints/bools/null included), the default otherwise.
fn raw_field(response: &Value, key: &str, default: &str) -> Value {
response.get(key).cloned().unwrap_or_else(|| json!(default))
}
fn usage_field(usage: Option<&Value>, key: &str) -> Value {
usage.and_then(|u| u.get(key)).cloned().unwrap_or(json!(0))
}
pub fn parse_mimo_response(response: &Value) -> RuneResponse {
let reasoning = str_field(response, "reasoning_content");
let content = str_field(response, "content");
let usage = response.get("usage");
let mut provenance = Map::new();
provenance.insert("provider".to_string(), json!("mimo"));
provenance.insert("model".to_string(), raw_field(response, "model", "unknown"));
provenance.insert(
"reasoning_tokens".to_string(),
usage_field(usage, "reasoning_tokens"),
);
provenance.insert(
"completion_tokens".to_string(),
usage_field(usage, "completion_tokens"),
);
provenance.insert(
"total_tokens".to_string(),
usage_field(usage, "total_tokens"),
);
RuneResponse {
user_text: content,
debug_refs: if !reasoning.is_empty() {
vec![serde_json::json!({"type": "reasoning", "content": reasoning})]
} else {
debug_refs: if reasoning.is_empty() {
vec![]
} else {
vec![json!({"type": "reasoning", "content": reasoning})]
},
provenance,
..Default::default()
}
}
pub fn parse_qwen_response(response: &serde_json::Value) -> RuneResponse {
let content = response
.get("content")
.and_then(|c| c.as_str())
.unwrap_or("");
let think_re = regex::Regex::new(r"(?s)<think>(.*?)</think>").unwrap();
pub fn parse_qwen_response(response: &Value) -> RuneResponse {
let content = str_field(response, "content");
let think_re = regex::Regex::new(r"(?s)<think>(.*?)</think>").expect("think pattern");
let think_matches: Vec<String> = think_re
.captures_iter(content)
.captures_iter(&content)
.map(|c| c[1].to_string())
.collect();
let visible = think_re.replace_all(content, "").trim().to_string();
let model = response
.get("model")
.and_then(|m| m.as_str())
.unwrap_or("unknown")
.to_string();
let mut provenance = std::collections::BTreeMap::new();
provenance.insert("provider".to_string(), serde_json::json!("qwen"));
provenance.insert("model".to_string(), serde_json::json!(model));
let visible = think_re.replace_all(&content, "").trim().to_string();
let usage = response.get("usage");
let reasoning_words: usize = think_matches
.iter()
.map(|t| t.split_whitespace().count())
.sum();
let mut provenance = Map::new();
provenance.insert("provider".to_string(), json!("qwen"));
provenance.insert("model".to_string(), raw_field(response, "model", "unknown"));
provenance.insert("reasoning_words".to_string(), json!(reasoning_words));
provenance.insert(
"completion_tokens".to_string(),
usage_field(usage, "completion_tokens"),
);
provenance.insert(
"total_tokens".to_string(),
usage_field(usage, "total_tokens"),
);
RuneResponse {
user_text: visible,
debug_refs: think_matches
.iter()
.map(|t| serde_json::json!({"type": "think", "content": t}))
.map(|t| json!({"type": "think", "content": t}))
.collect(),
provenance,
..Default::default()
}
}
pub fn parse_local_response(response: &serde_json::Value) -> RuneResponse {
let content = response
.get("content")
.and_then(|c| c.as_str())
.unwrap_or("");
let thinking_re = regex::Regex::new(r"(?s)Thinking Process:\s*(.*?)\s*Final:\s*(.*)").unwrap();
let model = response
.get("model")
.and_then(|m| m.as_str())
.unwrap_or("unknown")
.to_string();
let mut provenance = std::collections::BTreeMap::new();
provenance.insert("provider".to_string(), serde_json::json!("local"));
provenance.insert("model".to_string(), serde_json::json!(model));
if let Some(m) = thinking_re.captures(content) {
pub fn parse_local_response(response: &Value) -> RuneResponse {
let content = str_field(response, "content");
let thinking_re = regex::Regex::new(r"(?s)Thinking Process:\s*(.*?)\s*Final:\s*(.*)")
.expect("thinking pattern");
let base_provenance = || {
let mut p = Map::new();
p.insert("provider".to_string(), json!("local"));
p.insert("model".to_string(), raw_field(response, "model", "unknown"));
p
};
if let Some(m) = thinking_re.captures(&content) {
let thinking = m[1].trim().to_string();
let final_text = m[2].trim().to_string();
let mut provenance = base_provenance();
provenance.insert(
"reasoning_tokens".to_string(),
json!(thinking.split_whitespace().count()),
);
return RuneResponse {
user_text: m[2].trim().to_string(),
debug_refs: vec![serde_json::json!({"type": "thinking", "content": m[1].trim()})],
user_text: final_text,
debug_refs: vec![json!({"type": "thinking", "content": thinking})],
provenance,
..Default::default()
};
}
RuneResponse {
user_text: content.to_string(),
user_text: content,
provenance: base_provenance(),
..Default::default()
}
}
pub fn parse_plain_response(response: &Value) -> RuneResponse {
let content = str_field(response, "content");
let mut provenance = Map::new();
provenance.insert(
"provider".to_string(),
raw_field(response, "provider", "unknown"),
);
provenance.insert("model".to_string(), raw_field(response, "model", "unknown"));
RuneResponse {
user_text: content,
provenance,
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_mimo() {
let r =
parse_mimo_response(&json!({"content": "hello", "reasoning_content": "thinking..."}));
assert_eq!(r.user_text, "hello");
assert_eq!(r.debug_refs.len(), 1);
}
#[test]
fn test_qwen() {
let r = parse_qwen_response(&json!({"content": "<think>reasoning</think>answer"}));
assert_eq!(r.user_text, "answer");
assert_eq!(r.debug_refs.len(), 1);
}
#[test]
fn test_local() {
let r = parse_local_response(
&json!({"content": "Thinking Process: analyze\nFinal: the answer"}),
);
assert_eq!(r.user_text, "the answer");
pub fn parse_response(response: &Value, provider: Option<&str>) -> RuneResponse {
let provider = match provider {
Some(p) => p.to_string(),
None => {
if response.get("reasoning_content").is_some() {
"mimo".to_string()
} else if str_field(response, "content").contains("<think>") {
"qwen".to_string()
} else if str_field(response, "content").contains("Thinking Process:") {
"local".to_string()
} else {
"plain".to_string()
}
}
};
match provider.as_str() {
"mimo" => parse_mimo_response(response),
"qwen" => parse_qwen_response(response),
"local" => parse_local_response(response),
_ => parse_plain_response(response),
}
}
#[cfg(test)]
mod branch_battery {
use super::*;
/// The oracle self-test fn: the same four provider samples through
/// parse_response with the same asserts (crash parity — an assert failure
/// panics here like AssertionError aborts the oracle).
pub fn test_adapter() -> Value {
let mimo_response = json!({
"reasoning_content": "Let me think about this...",
"content": "Hello! How can I help?",
"model": "mimo-v2.5-pro",
"usage": {"reasoning_tokens": 10, "completion_tokens": 8, "total_tokens": 18},
});
let result = parse_response(&mimo_response, Some("mimo"));
assert_eq!(result.user_text, "Hello! How can I help?");
assert_eq!(result.debug_refs.len(), 1);
#[test]
fn parse_plain_response_shape() {
let resp =
parse_local_response(&serde_json::json!({"content": "hello world", "model": "test"}));
assert_eq!(resp.user_text, "hello world");
let qwen_response = json!({
"content": "<think>\nLet me think about this...\n</think>\n\nHello! How can I help?",
"model": "qwen3:14b",
"usage": {"completion_tokens": 20},
});
let result = parse_response(&qwen_response, Some("qwen"));
assert_eq!(result.user_text, "Hello! How can I help?");
assert_eq!(result.debug_refs.len(), 1);
let local_response = json!({"content": "Thinking Process: I need to respond helpfully. Final: Hello!", "model": "qwen2.5:3b"});
let result = parse_response(&local_response, Some("local"));
assert_eq!(result.user_text, "Hello!");
let plain_response = json!({"content": "Hello!", "model": "gpt-4"});
let result = parse_response(&plain_response, Some("plain"));
assert_eq!(result.user_text, "Hello!");
Value::Null
}
// ── frozen-truth dispatch (battery + parity share this) ────────────────────
fn resp_arg(case: &Value) -> Result<Value, String> {
case.get("args")
.and_then(Value::as_array)
.and_then(|a| a.first().cloned())
.ok_or_else(|| "args[0] (response) required".to_string())
}
fn provider_arg(case: &Value) -> Option<String> {
let args = case.get("args").and_then(Value::as_array);
let kw = case.get("kwargs");
// positional args[1] wins over kwargs["provider"]; explicit null = None
match args
.and_then(|a| a.get(1))
.or_else(|| kw.and_then(|k| k.get("provider")))
{
Some(v) if !v.is_null() => v.as_str().map(str::to_string),
_ => None,
}
}
/// Replay one frozen-truth case ({fn, args, kwargs, recv?}) exactly the
/// way the shared harness froze it: parse fns return the to_dict form;
/// to_dict rows carry {result, <recv attrs…>} (the harness's post-call
/// receiver snapshot — to_dict never mutates, so attrs mirror the input).
pub fn dispatch(case: &Value) -> Result<Value, String> {
let fn_name = case
.get("fn")
.and_then(Value::as_str)
.ok_or_else(|| "case.fn required".to_string())?;
match fn_name {
"parse_mimo_response" => Ok(parse_mimo_response(&resp_arg(case)?).to_dict()),
"parse_qwen_response" => Ok(parse_qwen_response(&resp_arg(case)?).to_dict()),
"parse_local_response" => Ok(parse_local_response(&resp_arg(case)?).to_dict()),
"parse_plain_response" => Ok(parse_plain_response(&resp_arg(case)?).to_dict()),
"parse_response" => {
let provider = provider_arg(case);
Ok(parse_response(&resp_arg(case)?, provider.as_deref()).to_dict())
}
"to_dict" | "RuneResponse.to_dict" => {
let recv = case
.get("recv")
.and_then(Value::as_object)
.ok_or_else(|| "to_dict rows need recv".to_string())?;
let mut state = Map::new();
for (k, v) in recv {
if k != "cls" {
state.insert(k.clone(), v.clone());
}
}
let inst: RuneResponse = serde_json::from_value(Value::Object(state))
.map_err(|e| format!("recv rebuild: {e}"))?;
let mut expected = Map::new();
expected.insert("result".to_string(), inst.to_dict());
let snapshot = serde_json::to_value(&inst).map_err(|e| e.to_string())?;
if let Value::Object(snapshot) = snapshot {
for (k, v) in snapshot {
expected.insert(k, v);
}
}
Ok(Value::Object(expected))
}
"test_adapter" => Ok(test_adapter()),
other => Err(format!("unknown fn: {other}")),
}
}
// ── FFI ─────────────────────────────────────────────────────────────────────
fn read_arg(ptr: *const c_char) -> Result<Value, String> {
if ptr.is_null() {
return Err("null argument pointer".into());
}
let x = unsafe { CStr::from_ptr(ptr) }
.to_str()
.map_err(|e| e.to_string())?;
serde_json::from_str(x).map_err(|e| e.to_string())
}
fn envelope(r: Result<Value, String>) -> *mut c_char {
let v = match r {
Ok(x) => json!({"ok": true, "value": x}),
Err(e) => json!({"ok": false, "err": e}),
};
CString::new(serde_json::to_string(&v).unwrap_or_default())
.map(|c| c.into_raw())
.unwrap_or(std::ptr::null_mut())
}
/// ra_parse_response({"fn", "response", "provider"?}) → the parser's
/// to_dict form (the facade's delegation entry).
#[no_mangle]
pub extern "C" fn ra_parse_response(arg: *const c_char) -> *mut c_char {
let r = read_arg(arg).and_then(|a| {
let fn_name = a
.get("fn")
.and_then(Value::as_str)
.ok_or_else(|| "arg.fn required".to_string())?
.to_string();
let response = a
.get("response")
.cloned()
.ok_or_else(|| "arg.response required".to_string())?;
let case = if a.get("provider").is_some() {
json!({"fn": fn_name, "args": [response, a.get("provider").cloned().unwrap_or(Value::Null)]})
} else {
json!({"fn": fn_name, "args": [response]})
};
dispatch(&case)
});
envelope(r)
}
/// ra_replay(case) — the frozen-truth replay surface: {fn, args, kwargs,
/// recv?} → the frozen expected form. The parity script drives this.
#[no_mangle]
pub extern "C" fn ra_replay(arg: *const c_char) -> *mut c_char {
let r = read_arg(arg).and_then(|a| dispatch(&a));
envelope(r)
}
// NOTE: no free-symbol export (capability_scoring's cs_free_string serves
// the whole process — duplicate symbols break the cdylib link).
#[cfg(test)]
mod gap_battery;
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
0
+84
View File
@@ -0,0 +1,84 @@
# response_adapter dichotomy gap-close — verification summary
Module: response_adapter (audit 5/7 PARTIAL → 7/7 FULL, RUST).
Worktree: rune-wave-dg. Parity commit: see `git log` feat(response_adapter).
## Port surface (name-preserving, src/rust/response_adapter/src/lib.rs)
- `RuneResponse` + `to_dict` — ordered dict form (serde struct field order
= dataclass field order; serde_json preserve_order keeps provenance /
debug_refs insertion order — the json insertion-order quirk).
- `parse_mimo_response` — FULL parity this pass: usage pass-through fields
reasoning_tokens/completion_tokens/total_tokens (Python `.get(k, 0)`
semantics — ANY JSON value rides, not just ints), model pass-through.
- `parse_qwen_response` — reasoning_words (whitespace-run word count sum),
completion_tokens/total_tokens; (?s) lazy think-block regex identical
capture semantics.
- `parse_local_response` — greedy group-2 (text after the FIRST `Final:`
to end of string), `\s*` sandwich trims, reasoning_tokens word count.
- `parse_plain_response` — NOW A REAL FN (previously only a test name in
the corpus — the audit's substring match over-credited it); provider
pass-through from the response (default "unknown").
- `parse_response` — NOW A REAL FN: auto-detect (reasoning_content key
presence → mimo; "<think>" substring → qwen; "Thinking Process:" →
local; else plain), explicit provider dispatch, unknown provider →
plain parser.
- `test_adapter` — assert-parity self-test (same four samples, same
asserts; panic ≅ AssertionError crash parity).
## FFI + facade
- cdylib exports `ra_parse_response` ({fn, response, provider?} → parser
to_dict form) and `ra_replay` (frozen-truth case → expected form).
cs_free_string (capability_scoring) frees — no duplicate free symbol.
- `src/rune/ra_loader.py` (ctypes loader, RA_RUST_LIB override) +
facade delegation in `src/rune/response_adapter.py` for the 5 parse
fns with `RUNE_RESPONSE_ADAPTER_CORE=python` pin; `to_dict` /
`test_adapter` stay Python on the facade (pure projection / stdout
contract — W6 FFI-overhead law); kernel failures fall back, never
crash the caller.
## Truth (shared harness + rig)
- 36-case corpus `src/rust/response_adapter/frozen_ra_gap_truth.json`
via scripts/freeze_truth.py + scripts/ra_freeze_seams rig (pinned
RUNE_RESPONSE_ADAPTER_CORE=python; to_dict-carrying wrappers; the
cross-call law — parse_response resolves its parser dict from module
globals at call time and test_adapter reads .user_text, so the rig
restores originals around the self-test). No clock/HOME seams (module
is pure — no-clock law holds by construction).
- Battery `gap_battery.rs`: frozen corpus embedded VERBATIM, replayed
ORDER-SENSITIVE (serialized forms byte-match), 7 tests.
## Gates (evidence: verification/ra-*.exit, logs local *.log gitignored)
- ra-freeze-verify-1/2.exit = 0 — `freeze_truth.py --verify` byte-identical, twice
- ra-cargo-test.exit = 0 — battery 7/7 (36-case verbatim replay + FFI
roundtrip + order/auto-detect/assert branch proofs)
- ra-clippy.exit = 0 — clippy --all-targets -D warnings clean
- ra-fmt.exit = 0 — cargo fmt --check clean
- ra-build.exit = 0 — cargo build --release (cdylib builds)
- ra-parity.exit = 0 — scripts/check_response_adapter_parity.py:
FROZEN 36/36 verbatim through ra_replay (ordered compare) + FUZZ 2000
op comparisons seed 2479 (unicode, floats, nulls, nested tags,
whitespace runs) + PINS 10 facade calls identical under
RUNE_RESPONSE_ADAPTER_CORE=python vs live delegation
- ra-pysmoke-{python,auto}.exit = 0 — contracts run_all_contracts
response_adapter PASS + 7-fn facade smoke, both pins
- ra-pytest-{python,auto}.exit = 0 — tests/test_contracts_port.py green
both pins (11 skipped: the contracts crate itself not built — vacuous
for this module)
- ra-audit.exit = 0 — dichotomy_audit.py --write: response_adapter
7/7 FULL RUST; programme FULL 110/192
## Divergences (documented, out-of-contract inputs only)
1. Non-string `content`/`reasoning_content`: Python passes the raw value
through into user_text (dataclass untyped); Rust coerces to "" (the
struct field is String). Provider responses carry string content per
the module contract; fuzz corpus keeps strings.
2. Non-dict `usage` (e.g. a list): Python raises AttributeError at
`usage.get`; Rust treats it as absent (zeros). Crash parity for this
class is documented rather than reproduced — the frozen corpus keeps
dict/missing usage only.
3. Rust `str::trim` (Unicode White_Space) vs Python `str.strip()`
(isspace(): also \x1c-\x1f file separators). Frozen corpus and fuzz
alphabet use standard whitespace; the boundary chars are control-code
noise, not provider output.
4. Facade `to_dict`/`test_adapter` do not delegate (W6 FFI-overhead
law); the Rust counterparts exist and are battery/parity-proven.