feat(openai_compat_transport): port to FULL Go — dichotomy wave
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / documentation (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 0s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 19s
CI Pipeline / best-practices (push) Canceled after 1s
Auto-version and Release / release (push) Canceled after 27s
CI Pipeline / canary (push) Canceled after 21s
deploy / deploy (push) Canceled after 25s
CI Pipeline / lint (push) Canceled after 25s
CI Pipeline / unit-tests (push) Canceled after 25s

This commit is contained in:
Nick committed 2026-10-12 03:58:49 +01:00
1 parent ee411f8e6e
commit 2e3b7e76ec
10 files changed
+4627

No files matched your search

@@ -0,0 +1,526 @@
#!/usr/bin/env python3
"""Freeze truth for the openai_compat_transport Go port (dichotomy wave #2479).
Exercises EVERY public function of src/rune/openai_compat_transport.py against
the LIVE oracle: __init__ (timeout clamp), _post (wire + non-object guard),
_to_openai_messages (all branches), _from_openai_response (all branches),
chat (model split, tools, options, usage), supports_tools, supports_thinking,
context_length, is_cloud_pinned, cloud_transport_for (config/env/auth seams).
Wire IO served by a local stub HTTP server that captures each request
(path, authorization, content-type, body bytes) and returns scripted bodies;
cloud_transport_for's IO seams (os.path.expanduser, os.environ) are patched
to temp files so config/auth resolution is deterministic.
Excluded branches (interpreter-specific error text — not portable truth):
- _post on a non-2xx HTTP status (Python urllib HTTPError string vs Go
net/http error string) and on connection failures.
- _post on a body that is invalid JSON (Python json.JSONDecodeError text).
- cloud_transport_for with auth.json containing invalid JSON (same reason).
Numbers: fixtures are written by json.dump so every numeric literal is in
Python's canonical repr; the Go port preserves literals on parse. Floats in
fixtures stay in the shortest-decimal band (no exponents) so literal
preservation equals Python re-rendering.
Nondeterministic: usage_total_duration_ns (time.monotonic) is excluded from
every row; the Go battery asserts it is > 0 after a chat instead.
"""
import json
import os
import sys
import tempfile
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
import rune.openai_compat_transport as oct_mod # noqa: E402
# -- stub wire ---------------------------------------------------------------
class StubState:
def __init__(self):
self.script = [] # list of response body strings
self.requests = [] # captured requests, one dict per POST
def reset(self, bodies):
self.script = list(bodies)
self.requests = []
STUB = StubState()
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
STUB.requests.append({
"path": self.path,
"authorization": self.headers.get("Authorization"),
"content_type": self.headers.get("Content-Type"),
"body": body.decode("utf-8"),
})
resp = STUB.script.pop(0)
data = resp.encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def log_message(self, *args):
pass
def start_stub():
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
return server, f"http://127.0.0.1:{server.server_address[1]}"
# -- row helpers -------------------------------------------------------------
ROWS = []
def add(name, op, setup, want):
ROWS.append({"name": name, "op": op, "setup": setup, "want": want})
def attrs(t):
return {
"base_url": t.base_url,
"api_key": t.api_key,
"provider": t.provider,
"timeout": t.timeout,
"context_window": t._context_window,
"usage_calls": t.usage_calls,
"usage_prompt_tokens": t.usage_prompt_tokens,
"usage_output_tokens": t.usage_output_tokens,
"usage_total_duration_ns": t.usage_total_duration_ns,
}
# -- cases -------------------------------------------------------------------
def case_ctor():
add("ctor_defaults", "ctor",
{"base_url": "https://api.example.com/v1/", "api_key": "k1", "provider": "Verboo Code"},
attrs(oct_mod.OpenAICompatTransport("https://api.example.com/v1/", "k1", "Verboo Code")))
add("ctor_timeout_clamped_low", "ctor",
{"base_url": "http://x/v1", "api_key": "k", "provider": "T", "timeout": 30},
attrs(oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", timeout=30)))
add("ctor_timeout_clamp_boundary", "ctor",
{"base_url": "http://x/v1", "api_key": "k", "provider": "T", "timeout": 120},
attrs(oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", timeout=120)))
add("ctor_timeout_above_floor_float", "ctor",
{"base_url": "http://x/v1", "api_key": "k", "provider": "T", "timeout": 130.5, "context_window": 8192},
attrs(oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", timeout=130.5, context_window=8192)))
def case_to_messages():
f = oct_mod.OpenAICompatTransport._to_openai_messages
cases = [
("tm_empty", []),
("tm_plain_roles", [
{"role": "system", "content": "sys"},
{"role": "user", "content": "task"},
{"role": "assistant", "content": "hi"},
]),
("tm_missing_content_defaults_empty", [{"role": "user"}]),
("tm_null_content_stays_null", [{"role": "user", "content": None}]),
("tm_null_role", [{"content": "orphan"}]),
("tm_assistant_empty_tool_calls_is_plain", [
{"role": "assistant", "content": "text", "tool_calls": []},
]),
("tm_tool_call_dict_args", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "edit_file", "arguments": {"path": "/x.py", "old": "a", "new": "b"}}}]},
{"role": "tool", "tool_call_id": "call_2_0", "content": "{\"success\": true}"},
]),
("tm_tool_call_string_args", [
{"role": "assistant", "content": "working", "tool_calls": [
{"function": {"name": "read_file", "arguments": "{\"path\": \"/x.py\"}"}}]},
]),
("tm_tool_call_missing_args_defaults_empty_obj", [
{"role": "assistant", "content": None, "tool_calls": [{"function": {"name": "noop"}}]},
]),
("tm_tool_call_missing_function", [
{"role": "assistant", "content": None, "tool_calls": [{"id": "weird"}]},
]),
("tm_tool_call_non_dict_args_int", [
{"role": "assistant", "content": None, "tool_calls": [
{"function": {"name": "ping", "arguments": 42}}]},
]),
("tm_tool_call_non_dict_args_list", [
{"role": "assistant", "content": None, "tool_calls": [
{"function": {"name": "bulk", "arguments": ["a", "b"]}}]},
]),
("tm_tool_call_null_args_str", [
{"role": "assistant", "content": None, "tool_calls": [
{"function": {"name": "q", "arguments": "None"}}]},
]),
("tm_ids_paired_from_following_tools", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}, },
{"function": {"name": "b", "arguments": {}}, }]},
{"role": "tool", "tool_call_id": "call_0_0", "content": "r1"},
{"role": "tool", "tool_call_id": "call_0_1", "content": "r2"},
]),
("tm_ids_fallback_when_unpaired", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}},
{"function": {"name": "b", "arguments": {}}}]},
{"role": "tool", "tool_call_id": "call_0_0", "content": "r1"},
]),
("tm_ids_scan_nonadjacent_tools", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}},
{"function": {"name": "b", "arguments": {}}}]},
{"role": "user", "content": "and?"},
{"role": "tool", "tool_call_id": "aa", "content": "r1"},
{"role": "tool", "tool_call_id": "bb", "content": "r2"},
]),
("tm_paired_id_null_falls_back", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}}]},
{"role": "tool", "content": "no id"},
]),
("tm_tool_message_defaults", [
{"role": "tool", "content": "result"},
{"role": "tool", "tool_call_id": "t9", "content": "r"},
{"role": "tool"},
]),
("tm_mixed_conversation", [
{"role": "system", "content": "sys"},
{"role": "user", "content": "go"},
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "ls", "arguments": {"path": "."}}}]},
{"role": "tool", "tool_call_id": "call_2_0", "content": "files"},
{"role": "assistant", "content": "done"},
]),
]
for name, messages in cases:
add(name, "to_messages", {"messages": messages}, f(messages))
def case_from_response():
f = oct_mod.OpenAICompatTransport._from_openai_response
cases = [
("fr_plain_content", {"choices": [{"message": {"role": "assistant", "content": "hello"}}]}),
("fr_missing_choices", {}),
("fr_empty_choices", {"choices": []}),
("fr_missing_message", {"choices": [{}]}),
("fr_null_content_becomes_empty", {"choices": [{"message": {"content": None}}]}),
("fr_no_tool_calls_key", {"choices": [{"message": {"content": "x", "tool_calls": []}}]}),
("fr_null_tool_calls", {"choices": [{"message": {"content": "x", "tool_calls": None}}]}),
("fr_tool_call_string_args", {"choices": [{"message": {"tool_calls": [
{"id": "z", "type": "function",
"function": {"name": "run_tests", "arguments": "{\"path\": \"t\"}"}}]}}]}),
("fr_tool_call_malformed_args_raw", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "x", "arguments": "not-json"}}]}}]}),
("fr_tool_call_dict_args_kept", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "y", "arguments": {"a": 1}}}]}}]}),
("fr_tool_call_null_args_become_empty_obj", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "n", "arguments": None}}]}}]}),
("fr_tool_call_empty_string_args_raw", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "e", "arguments": ""}}]}}]}),
("fr_tool_call_list_args_kept", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "l", "arguments": [1, 2]}}]}}]}),
("fr_tool_call_id_only_when_present", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "a", "arguments": "{}"}},
{"id": "", "function": {"name": "b", "arguments": "{}"}},
{"id": "c3", "function": {"name": "c", "arguments": "{}"}}]}}]}),
("fr_tool_call_missing_function", {"choices": [{"message": {"tool_calls": [{}]}}]}),
("fr_multiple_tool_calls", {"choices": [{"message": {"content": None, "tool_calls": [
{"id": "i1", "function": {"name": "a", "arguments": "{\"x\": 1}"}},
{"id": "i2", "function": {"name": "b", "arguments": {"y": [2]}}}]}}]}),
("fr_choices_null_becomes_default", {"choices": None}),
]
for name, resp in cases:
add(name, "from_response", {"resp": resp}, f(resp))
def case_post(server_base):
t = oct_mod.OpenAICompatTransport(server_base, "sekrit", "Prov")
def run(path, payload, bodies):
STUB.reset(bodies)
try:
result = t._post(path, payload)
return {"request": STUB.requests[0], "result": result}
except ValueError as e:
return {"request": STUB.requests[0], "error": str(e)}
payload = {"model": "glm-5.3-flash", "messages": [{"role": "user", "content": "hi"}], "stream": False}
add("post_roundtrip", "post",
{"path": "/chat/completions", "payload": payload, "bodies": [
json.dumps({"choices": [{"message": {"content": "ok"}}], "usage": {"prompt_tokens": 5, "completion_tokens": 7}})]},
run("/chat/completions", payload, [json.dumps({"choices": [{"message": {"content": "ok"}}], "usage": {"prompt_tokens": 5, "completion_tokens": 7}})]))
add("post_non_object_list", "post",
{"path": "/x", "payload": {"a": 1}, "bodies": ["[1, 2]"]},
run("/x", {"a": 1}, ["[1, 2]"]))
add("post_non_object_null", "post",
{"path": "/x", "payload": {}, "bodies": ["null"]},
run("/x", {}, ["null"]))
add("post_non_object_string", "post",
{"path": "/x", "payload": {}, "bodies": ['"text"']},
run("/x", {}, ['"text"']))
def case_chat(server_base):
cases = [
("chat_qualified_model_bare_on_wire", {
"model": "Verboo Code/glm-5.3-flash",
"messages": [{"role": "user", "content": "hi"}],
"tools": None, "options": None,
"responses": ['{"choices": [{"message": {"content": "ok"}}], "usage": {}}'],
}),
("chat_bare_model_unchanged", {
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "hi"}],
"tools": None, "options": None,
"responses": ['{"choices": [{"message": {"content": "ok"}}]}'],
}),
("chat_tools_passthrough", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": [{"type": "function", "function": {"name": "ls", "description": "list", "parameters": {"type": "object"}}}],
"options": None,
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_empty_tools_omitted", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": [], "options": None,
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_options_subset", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": {"temperature": 0.7, "top_p": 0.9, "max_tokens": 1024, "seed": 7, "stop": ["END"]},
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_options_partial_and_empty", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": {"max_tokens": 0},
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_think_ignored", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None, "think": True,
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_think_string_ignored", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None, "think": "high",
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_tool_call_roundtrip", {
"model": "Verboo Code/m1", "messages": [
{"role": "user", "content": "go"},
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "edit_file", "arguments": {"path": "/x.py"}}}]},
{"role": "tool", "tool_call_id": "call_1_0", "content": "done"},
],
"tools": [{"type": "function", "function": {"name": "edit_file"}}], "options": {"temperature": 0.2},
"responses": [json.dumps({"choices": [{"message": {"content": None, "tool_calls": [
{"id": "call_1_0", "function": {"name": "edit_file",
"arguments": json.dumps({"path": "/x.py"})}}]}}],
"usage": {"prompt_tokens": 100, "completion_tokens": 20}})],
}),
("chat_usage_string_and_float_coerced", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None,
"responses": ['{"choices": [{"message": {"content": "x"}}], "usage": {"prompt_tokens": "12", "completion_tokens": 3.9}}'],
}),
("chat_non_object_response_raises", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None,
"responses": ["[]"],
}),
("chat_two_calls_accumulate_usage", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None, "calls": 2,
"responses": [
'{"choices": [{"message": {"content": "a"}}], "usage": {"prompt_tokens": 10, "completion_tokens": 4}}',
'{"choices": [{"message": {"content": "b"}}], "usage": {"prompt_tokens": 7, "completion_tokens": 9}}',
],
}),
]
for name, c in cases:
t = oct_mod.OpenAICompatTransport(server_base, "wire-key-1", "Prov")
STUB.reset(c["responses"])
want = {}
try:
for _ in range(c.get("calls", 1)):
result = t.chat(model=c["model"], messages=c["messages"], tools=c["tools"],
options=c["options"], think=c.get("think"))
want["result"] = result
except ValueError as e:
want["error"] = str(e)
want["requests"] = list(STUB.requests)
want["usage"] = {"calls": t.usage_calls, "prompt_tokens": t.usage_prompt_tokens,
"output_tokens": t.usage_output_tokens}
add(name, "chat",
{"base_url": "@STUB@", "api_key": "wire-key-1", "provider": "Prov",
"model": c["model"], "messages": c["messages"], "tools": c["tools"],
"options": c["options"], "think": c.get("think"), "calls": c.get("calls", 1),
"responses": c["responses"]},
want)
def case_scalars():
t = oct_mod.OpenAICompatTransport("http://x/v1", "k", "T")
add("supports_tools_true", "scalar", {"fn": "supports_tools", "model": "anything"}, t.supports_tools("anything"))
add("supports_thinking_false", "scalar", {"fn": "supports_thinking", "model": "anything"}, t.supports_thinking("anything"))
add("context_length_default", "scalar", {"fn": "context_length", "model": "m"}, t.context_length("m"))
t2 = oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", context_window=400000)
add("context_length_custom", "scalar", {"fn": "context_length", "model": "m", "context_window": 400000}, t2.context_length("m"))
icp = oct_mod.is_cloud_pinned
pinned = [
("icp_cloud_provider", "Verboo Code/glm-5.3-flash", True),
("icp_ollama_prefix", "ollama/qwen2.5:3b", False),
("icp_local_prefix", "local/qwen", False),
("icp_ollama_case_insensitive", "OLLAMA/qwen", False),
("icp_local_case_insensitive", "Local/qwen", False),
("icp_bare_model", "qwen2.5:3b", False),
("icp_empty_prefix", "/x", False),
("icp_prefix_whitespace_trimmed", " zhipu /glm", True),
("icp_prefix_lowered", "ZiPu/glm", True),
("icp_nested_slash_ollama", "ollama/x/y", False),
("icp_local_nested_slash", "local/x/y", False),
("icp_unknown_nested", "a/b/c", True),
]
for name, model, expected in pinned:
add(name, "scalar", {"fn": "is_cloud_pinned", "model": model}, icp(model))
assert icp(model) == expected, name
def case_cloud(tmp_root):
def make_env(config_text, auth_text):
home = Path(tempfile.mkdtemp(dir=tmp_root))
cfg_dir = home / ".config" / "mimocode"
auth_dir = home / ".local" / "share" / "mimocode"
cfg_dir.mkdir(parents=True)
auth_dir.mkdir(parents=True)
if config_text is not None:
(cfg_dir / "mimocode.jsonc").write_text(config_text)
if auth_text is not None:
(auth_dir / "auth.json").write_text(auth_text)
return str(home), config_text, auth_text
def run(name, model, made, env, timeout):
home, config_text, auth_text = made
captured_env = {k: v for k, v in env.items()}
real_expand = os.path.expanduser
def fake_expand(p):
if p == "~":
return home
if p.startswith("~/"):
return home + p[1:]
return p
home_files = {}
if config_text is not None:
home_files["/.config/mimocode/mimocode.jsonc"] = config_text
if auth_text is not None:
home_files["/.local/share/mimocode/auth.json"] = auth_text
os.path.expanduser = fake_expand
try:
# clear=True: the real environment must not leak provider keys
# into frozen truth (LookupEnv would otherwise see them).
with mock.patch.dict(os.environ, captured_env, clear=True):
try:
t = oct_mod.cloud_transport_for(model, timeout=timeout)
want = attrs(t)
except ValueError as e:
want = {"error": str(e)}
finally:
os.path.expanduser = real_expand
add(name, "cloud",
{"model": model, "timeout": timeout, "home_files": home_files, "env": captured_env},
want)
config_full = """{
// provider registry — comments must not break extraction
"zhipu": {
"options": { "baseURL": "https://open.zhipu/v1" },
"models": {
"glm-5.3-flash": { "context_window": 400000 }
}
},
"Verboo Code": {
"options": { "baseURL": "https://verboo.example/v1" }
}
}
"""
run("cloud_full_config_env_key", "zhipu/glm-5.3-flash",
make_env(config_full, None), {"ZHIPU_API_KEY": "env-key-1"}, 120)
run("cloud_auth_json_key", "Verboo Code/m1",
make_env(config_full, json.dumps({"Verboo Code": {"key": "auth-key-9"}})), {}, 120)
run("cloud_env_empty_falls_to_auth", "zhipu/glm-5.3-flash",
make_env(config_full, json.dumps({"zhipu": {"key": "auth-key-2"}})),
{"ZHIPU_API_KEY": ""}, 120)
run("cloud_no_key_raises", "zhipu/glm-5.3-flash",
make_env(config_full, None), {}, 120)
run("cloud_no_config_raises_baseurl", "zhipu/glm-5.3-flash",
make_env(None, None), {"ZHIPU_API_KEY": "k"}, 120)
run("cloud_unknown_provider_raises_baseurl", "nosuch/glm",
make_env(config_full, None), {}, 120)
run("cloud_bare_model_raises", "glm-5.3-flash",
make_env(config_full, None), {}, 120)
run("cloud_timeout_floored_at_300", "zhipu/glm-5.3-flash",
make_env(config_full, None), {"ZHIPU_API_KEY": "k"}, 50)
run("cloud_timeout_above_floor_kept", "zhipu/glm-5.3-flash",
make_env(config_full, None), {"ZHIPU_API_KEY": "k"}, 500)
run("cloud_model_without_context_entry_default_window", "Verboo Code/m1",
make_env(config_full, None), {}, 120)
# baseURL beyond the 2000-char block window → not found
far_block = '{"big": {"options": {' + '"pad": "' + "x" * 2100 + '", ' + '"baseURL": "https://far/v1"}}}'
run("cloud_baseurl_beyond_block_window", "big/m",
make_env(far_block, None), {}, 120)
# -- main --------------------------------------------------------------------
def main():
out_path = ROOT / "src" / "go" / "openai_compat_transport" / "truth.json"
out_path.parent.mkdir(parents=True, exist_ok=True)
server, base = start_stub()
try:
case_ctor()
case_to_messages()
case_from_response()
case_post(base)
case_chat(base)
case_scalars()
tmp_root = tempfile.mkdtemp(prefix="oct_truth_")
case_cloud(tmp_root)
finally:
server.shutdown()
doc = {
"oracle": "src/rune/openai_compat_transport.py",
"generator": "scripts/gen_openai_compat_transport_truth.py",
"note": "usage_total_duration_ns excluded (monotonic clock); wire served by stub, "
"cloud IO seams patched to temp dirs; see generator docstring for excluded branches",
"rows": ROWS,
}
out_path.write_text(json.dumps(doc, indent=1) + "\n")
ops = {}
for r in ROWS:
ops[r["op"]] = ops.get(r["op"], 0) + 1
print(f"frozen {len(ROWS)} rows → {out_path}")
print("ops:", json.dumps(ops, sort_keys=True))
if __name__ == "__main__":
main()
+3
View File
@@ -0,0 +1,3 @@
module rune/openai-compat-transport
go 1.22
+28
View File
@@ -0,0 +1,28 @@
// Package main — OpenAI-compat transport CLI: replay the frozen oracle
// truth battery.
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: openai-compat-transport replay <truth.json>")
os.Exit(2)
}
path := "truth.json"
if len(os.Args) > 2 {
path = os.Args[2]
}
fails := RunTruth(path)
if len(fails) > 0 {
for _, f := range fails {
fmt.Println(f)
}
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", len(fails))
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases exact-equal vs the Python oracle\n", truthRowCount())
}
@@ -0,0 +1,586 @@
// Package main — OpenAI-compatible chat transport, ported 1:1 from
// src/rune/openai_compat_transport.py (dichotomy wave #2479).
//
// The transport speaks the OpenAI wire format (/chat/completions) and returns
// Ollama-SHAPED responses, so LocalToolRuntime's loop is unchanged. All IO
// sits behind injectable seams (HTTPPoster for the wire; ExpandHome /
// ReadFileText / LookupEnv for cloud_transport_for's config resolution).
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
// ValueError — the oracle raises Python's ValueError; error text is frozen
// truth and must match str(e) exactly.
type ValueError struct{ Msg string }
func (e *ValueError) Error() string { return e.Msg }
// HTTPPoster — the wire seam. Post sends body to url with the given
// headers and per-request timeout, returning the raw response body.
type HTTPPoster interface {
Post(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error)
}
// HTTPPosterFunc adapts a function to HTTPPoster.
type HTTPPosterFunc func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error)
// Post implements HTTPPoster.
func (f HTTPPosterFunc) Post(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
return f(url, headers, body, timeoutSecs)
}
// defaultPoster — net/http faithful to the oracle's urllib round trip.
type defaultPoster struct{}
func (defaultPoster) Post(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(string(body)))
if err != nil {
return nil, err
}
for k, v := range headers {
req.Header.Set(k, v)
}
client := &http.Client{Timeout: time.Duration(timeoutSecs * float64(time.Second))}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
buf := new(strings.Builder)
tmp := make([]byte, 32*1024)
for {
n, rerr := resp.Body.Read(tmp)
if n > 0 {
buf.Write(tmp[:n])
}
if rerr != nil {
break
}
}
return []byte(buf.String()), nil
}
// OpenAICompatTransport — ChatTransport for OpenAI-compatible providers
// (Verboo Code, Xioami...).
type OpenAICompatTransport struct {
BaseURL string
APIKey string
Provider string
// Cloud round trips with a full tool schema routinely exceed the local
// tier timeout (30s): observed live, glm-5.3-flash read-timeout at
// 30.6s mid-iteration. The constructor clamps the WIRE timeout to
// >=120s — the escalation ladder still governs retries/tiers.
Timeout float64
contextWindow int
usageCalls int
usagePromptTokens int
usageOutputTokens int
usageTotalDurationNS int64
poster HTTPPoster
// mu guards the usage accumulators once Phase B makes concurrent
// chats legal (ChatAll).
mu sync.Mutex
}
// NewOpenAICompatTransport — oracle defaults: timeout 120, context window 131072.
func NewOpenAICompatTransport(baseURL, apiKey, provider string) *OpenAICompatTransport {
return NewOpenAICompatTransportWith(baseURL, apiKey, provider, 120, 131072)
}
// NewOpenAICompatTransportWith — full constructor contract.
func NewOpenAICompatTransportWith(baseURL, apiKey, provider string, timeout float64, contextWindow int) *OpenAICompatTransport {
if timeout < 120 {
timeout = 120
}
return &OpenAICompatTransport{
BaseURL: strings.TrimRight(baseURL, "/"),
APIKey: apiKey,
Provider: provider,
Timeout: timeout,
contextWindow: contextWindow,
poster: defaultPoster{},
}
}
// SetPoster swaps the wire seam (battery/replay injection).
func (t *OpenAICompatTransport) SetPoster(p HTTPPoster) { t.poster = p }
// wirePath posts to base_url + path with the OpenAI auth headers, returning
// the parsed body (oracle _post).
func (t *OpenAICompatTransport) wirePost(path string, payload PyValue) (PyValue, error) {
url := t.BaseURL + path
headers := map[string]string{
"Content-Type": "application/json",
"Authorization": fmt.Sprintf("Bearer %s", t.APIKey),
}
body := []byte(PyDumpsCompact(payload))
raw, err := t.poster.Post(url, headers, body, t.Timeout)
if err != nil {
return nil, err
}
decoded, err := PyParse(raw)
if err != nil {
return nil, err
}
if _, isObj := decoded.(*PyObj); !isObj {
return nil, &ValueError{Msg: "provider returned a non-object response"}
}
return decoded, nil
}
// toOpenaiMessages — Ollama-shape → OpenAI-shape. Pairing: the runtime's
// _tool_message sets tool_call_id from request.call_id (minted by
// normalize_ollama_tool_calls from the response's own id). The assistant
// message must use THE SAME ids for its tool_calls — a mismatch makes the
// provider treat the tool result as orphaned and the model re-issues the
// identical call forever (observed live: glm-5.3-flash looped the same
// edit_file twice, empty terminal text both turns).
func toOpenaiMessages(messages []PyValue) []PyValue {
out := []PyValue{}
for i := 0; i < len(messages); i++ {
m, ok := messages[i].(*PyObj)
if !ok {
m = NewPyObj()
}
role, _ := m.Get("role")
roleStr, _ := role.(string)
tcRaw, _ := m.Get("tool_calls")
var toolCalls []PyValue
if list, isList := tcRaw.([]PyValue); isList {
toolCalls = list
}
if roleStr == "assistant" && len(toolCalls) > 0 {
// Look ahead: pair each tool_call with the tool messages that
// immediately follow, reusing their tool_call_ids.
var pairedIDs []PyValue
for j := i + 1; j < len(messages); j++ {
if len(pairedIDs) >= len(toolCalls) {
break
}
tm, ok := messages[j].(*PyObj)
if !ok {
continue
}
tmRole, _ := tm.Get("role")
if s, isStr := tmRole.(string); !isStr || s != "tool" {
continue
}
id, _ := tm.Get("tool_call_id")
pairedIDs = append(pairedIDs, id)
}
tc := []PyValue{}
for k, c := range toolCalls {
co, ok := c.(*PyObj)
if !ok {
co = NewPyObj()
}
fnRaw, _ := co.Get("function")
fn, _ := fnRaw.(*PyObj)
if fn == nil {
fn = NewPyObj()
}
argsRaw, _ := fn.Get("arguments")
if argsRaw == nil {
if _, present := fn.Get("arguments"); !present {
argsRaw = NewPyObj() // fn.get("arguments", {}) default
}
}
var argsStr string
if _, isObj := argsRaw.(*PyObj); isObj {
argsStr = PyDumpsCompact(argsRaw)
} else {
argsStr = PyStr(argsRaw)
}
name, _ := fn.Get("name")
var callID PyValue
if k < len(pairedIDs) {
callID = pairedIDs[k]
}
if !PyTruthy(callID) {
callID = fmt.Sprintf("call_%d_%d", i, k)
}
entry := NewPyObj()
entry.Set("id", callID)
entry.Set("type", "function")
fnObj := NewPyObj()
fnObj.Set("name", name)
fnObj.Set("arguments", argsStr)
entry.Set("function", fnObj)
tc = append(tc, entry)
}
am := NewPyObj()
am.Set("role", "assistant")
// m.get("content") or None — key may be missing (nil) or falsy.
content, _ := m.Get("content")
if !PyTruthy(content) {
content = nil
}
am.Set("content", content)
am.Set("tool_calls", tc)
out = append(out, am)
continue
}
if roleStr == "tool" {
tm := NewPyObj()
tm.Set("role", "tool")
// m.get("tool_call_id") or f"call_{i}" — missing AND null
// both fall back (Get returns nil for both).
id, _ := m.Get("tool_call_id")
if !PyTruthy(id) {
id = fmt.Sprintf("call_%d", i)
}
tm.Set("tool_call_id", id)
// m.get("content", "") — missing key defaults; explicit null
// survives (both arrive as nil from Get, but the oracle's
// frozen cases with null vs missing both render null...).
// Corrected below: distinguish via key presence.
content := toolContent(m)
tm.Set("content", content)
out = append(out, tm)
continue
}
pm := NewPyObj()
pm.Set("role", role)
content := plainContent(m)
pm.Set("content", content)
out = append(out, pm)
}
return out
}
// toolContent — oracle m.get("content", "") for tool messages.
func toolContent(m *PyObj) PyValue {
if _, present := m.Get("content"); !present {
return ""
}
v, _ := m.Get("content")
return v
}
// plainContent — oracle m.get("content", "") for plain roles.
func plainContent(m *PyObj) PyValue {
if _, present := m.Get("content"); !present {
return ""
}
v, _ := m.Get("content")
return v
}
// _from_openai_response — name-preserving alias for the audit's
// snake_case surface (the oracle's static method name verbatim).
func _from_openai_response(resp PyValue) PyValue { return fromOpenaiResponse(resp) }
// _to_openai_messages — name-preserving alias for the audit's snake_case
// surface (the oracle's static method name verbatim).
func _to_openai_messages(messages []PyValue) []PyValue { return toOpenaiMessages(messages) }
// _post — name-preserving alias for the audit's snake_case surface
// (the oracle's wire method name verbatim).
func _post(t *OpenAICompatTransport, path string, payload PyValue) (PyValue, error) {
return t.wirePost(path, payload)
}
// fromOpenaiResponse — OpenAI response → flat Ollama ChatMessage
// (LocalToolRuntime's contract).
//
// CONTRACT: chat() returns the MESSAGE object — role/content/tool_calls at
// top level — exactly like OllamaChatTransport.chat. Observed live (PL1
// Task A, 2026-09-12): the envelope shape {'message': {...}} made the
// runtime see 'empty terminal visible text' while the model had returned a
// valid read_file tool call.
func fromOpenaiResponse(resp PyValue) PyValue {
ro, ok := resp.(*PyObj)
if !ok {
ro = NewPyObj()
}
choicesRaw, _ := ro.Get("choices")
choices, isList := choicesRaw.([]PyValue)
if !isList || len(choices) == 0 {
choices = []PyValue{NewPyObj()}
}
first, _ := choices[0].(*PyObj)
if first == nil {
first = NewPyObj()
}
msgRaw, _ := first.Get("message")
message, _ := msgRaw.(*PyObj)
if message == nil {
message = NewPyObj()
}
tcRaw, _ := message.Get("tool_calls")
tcList, _ := tcRaw.([]PyValue)
toolCalls := []PyValue{}
for _, c := range tcList {
co, ok := c.(*PyObj)
if !ok {
co = NewPyObj()
}
fnRaw, _ := co.Get("function")
fn, _ := fnRaw.(*PyObj)
if fn == nil {
fn = NewPyObj()
}
argsRaw, _ := fn.Get("arguments")
if s, isStr := argsRaw.(string); isStr {
parsed, err := PyParse([]byte(s))
if err != nil {
raw := NewPyObj()
raw.Set("_raw", s)
argsRaw = raw
} else {
argsRaw = parsed
}
}
if !PyTruthy(argsRaw) {
argsRaw = NewPyObj()
}
name, _ := fn.Get("name")
fnObj := NewPyObj()
fnObj.Set("name", name)
fnObj.Set("arguments", argsRaw)
entry := NewPyObj()
entry.Set("function", fnObj)
// oracle: if c.get("id") — any truthy value is preserved.
if idRaw, _ := co.Get("id"); PyTruthy(idRaw) {
entry.Set("id", idRaw)
}
toolCalls = append(toolCalls, entry)
}
out := NewPyObj()
out.Set("role", "assistant")
contentRaw, _ := message.Get("content")
content := ""
if s, isStr := contentRaw.(string); isStr && s != "" {
content = s
}
out.Set("content", content)
if len(toolCalls) > 0 {
out.Set("tool_calls", toolCalls)
}
return out
}
// chatInput — the chat() keyword contract.
type chatInput struct {
Model string
Messages []PyValue
Tools PyValue
Options *PyObj
Think PyValue
}
// chat implements the oracle chat(): translate, post, accumulate usage,
// flatten the response.
func (t *OpenAICompatTransport) chat(in chatInput) (PyValue, error) {
// The runtime passes the qualified name ('<Provider>/<model>'); the
// provider API wants the bare model id ('glm-5.3-flash'). Observed
// live: qualified name → 400 on every call (PL1 held-out attempt).
wireModel := in.Model
if idx := strings.Index(in.Model, "/"); idx >= 0 {
wireModel = in.Model[idx+1:]
}
payload := NewPyObj()
payload.Set("model", wireModel)
payload.Set("messages", toOpenaiMessages(in.Messages))
payload.Set("stream", false)
if PyTruthy(in.Tools) {
payload.Set("tools", in.Tools) // Ollama tool schema == OpenAI tool schema
}
if in.Options != nil {
for _, k := range []string{"temperature", "top_p", "max_tokens"} {
if v, present := in.Options.Get(k); present {
payload.Set(k, v)
}
}
}
started := time.Now()
resp, err := t.wirePost("/chat/completions", payload)
elapsedNS := time.Since(started).Nanoseconds()
if err != nil {
return nil, err
}
usageRaw, _ := resp.(*PyObj).Get("usage")
usage, _ := usageRaw.(*PyObj)
if usage == nil {
usage = NewPyObj()
}
t.mu.Lock()
t.usageCalls++
t.usagePromptTokens += pyIntOr(usage, "prompt_tokens")
t.usageOutputTokens += pyIntOr(usage, "completion_tokens")
t.usageTotalDurationNS += elapsedNS
t.mu.Unlock()
return fromOpenaiResponse(resp), nil
}
// pyIntOr — Python int(usage.get(key) or 0) coercion: falsy → 0,
// int strings parse (floats like 3.9 truncate toward zero), bools → 0/1.
func pyIntOr(o *PyObj, key string) int {
v, _ := o.Get(key)
if !PyTruthy(v) {
return 0
}
switch t := v.(type) {
case json.Number:
if f, err := t.Float64(); err == nil {
return int(f)
}
case string:
if n, err := strconv.Atoi(strings.TrimSpace(t)); err == nil {
return n
}
if f, err := strconv.ParseFloat(strings.TrimSpace(t), 64); err == nil {
return int(f)
}
case bool:
if t {
return 1
}
}
return 0
}
// supportsTools — ChatTransport protocol.
func (t *OpenAICompatTransport) supportsTools(model string) bool { return true }
// supportsThinking — ChatTransport protocol.
func (t *OpenAICompatTransport) supportsThinking(model string) bool { return false }
// contextLength — ChatTransport protocol.
func (t *OpenAICompatTransport) contextLength(model string) int { return t.contextWindow }
// IsCloudPinned — THE provider-prefix rule, single authority: true when
// the model name requires the OpenAI-compat wire — a non-empty provider
// prefix that is neither ollama nor local. Consumers: transport selection
// and context discovery. Never copy this rule — import it.
func IsCloudPinned(model string) bool {
prefix := ""
if strings.Contains(model, "/") {
prefix = strings.ToLower(strings.TrimSpace(strings.SplitN(model, "/", 2)[0]))
}
return prefix != "" && prefix != "ollama" && prefix != "local"
}
// Cloud resolution seams (oracle: os.path.expanduser + Path.is_file +
// Path.read_text + os.environ.get). Injectable for the frozen battery.
var (
// ExpandHome resolves a leading "~" to the operator home.
ExpandHome = func(p string) string {
if p == "~" || strings.HasPrefix(p, "~/") {
home, err := os.UserHomeDir()
if err == nil {
if p == "~" {
return home
}
return filepath.Join(home, p[2:])
}
}
return p
}
// ReadFileText returns (text, exists, err) for a config file.
ReadFileText = func(path string) (string, bool, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return "", false, nil
}
return "", false, err
}
return string(data), true, nil
}
// LookupEnv mirrors os.environ.get(key, "") semantics: a set-but-empty
// variable yields "" (falsy) and falls through to auth.json.
LookupEnv = func(key string) (string, bool) { return os.LookupEnv(key) }
)
var (
providerBlockRe = func(provider string) *regexp.Regexp {
return regexp.MustCompile(`"` + regexp.QuoteMeta(provider) + `"\s*:\s*\{`)
}
baseURLRe = regexp.MustCompile(`"baseURL":\s*"([^"]+)"`)
// RE2 cannot bound the lookahead the way Python's re does, but the
// oracle searches within a 2000-char block — reproduced by slicing.
contextWindowReFor = func(shortModel string) *regexp.Regexp {
return regexp.MustCompile(`"` + regexp.QuoteMeta(shortModel) + `"\s*:\s*\{[^}]*?"context_window"\s*:\s*(\d+)`)
}
)
// CloudTransportFor — resolve provider config for a '<Provider>/<model>'
// name from the same source of truth the CLI uses (mimocode.jsonc +
// auth.json).
func CloudTransportFor(model string, timeout float64) (*OpenAICompatTransport, error) {
if !strings.Contains(model, "/") {
return nil, &ValueError{Msg: fmt.Sprintf("cloud model must be '<Provider>/<model>': %s", PyRepr(model))}
}
parts := strings.SplitN(model, "/", 2)
providerName, shortModel := parts[0], parts[1]
baseURL := ""
contextWindow := 131072
cfgPath := filepath.ToSlash(ExpandHome("~/.config/mimocode/mimocode.jsonc"))
if text, exists, err := ReadFileText(cfgPath); err == nil && exists {
if loc := providerBlockRe(providerName).FindStringIndex(text); loc != nil {
end := loc[0] + 2000
if end > len(text) {
end = len(text)
}
block := text[loc[0]:end]
if m := baseURLRe.FindStringSubmatch(block); m != nil {
baseURL = m[1]
}
if m := contextWindowReFor(shortModel).FindStringSubmatch(block); m != nil {
if n, err := strconv.Atoi(m[1]); err == nil {
contextWindow = n
}
}
}
}
if baseURL == "" {
return nil, &ValueError{Msg: fmt.Sprintf("no baseURL configured for provider %s", PyRepr(providerName))}
}
envKey := strings.ReplaceAll(strings.ToUpper(providerName), " ", "_") + "_API_KEY"
apiKey := ""
if v, _ := LookupEnv(envKey); v != "" {
apiKey = v
}
if apiKey == "" {
authPath := filepath.ToSlash(ExpandHome("~/.local/share/mimocode/auth.json"))
if text, exists, err := ReadFileText(authPath); err == nil && exists {
auth, perr := PyParse([]byte(text))
if perr == nil {
if ao, ok := auth.(*PyObj); ok {
entryRaw, _ := ao.Get(providerName)
entry, _ := entryRaw.(*PyObj)
if entry != nil {
if k, present := entry.Get("key"); present {
if s, isStr := k.(string); isStr {
apiKey = s
}
}
}
}
}
}
}
if apiKey == "" {
return nil, &ValueError{Msg: fmt.Sprintf("no API key for provider %s", PyRepr(providerName))}
}
// Cloud routers queue and batch — a tool-loop turn can legitimately
// exceed the local-model timeout (observed live: glm-5.3-flash
// read-timeout at 120s twice). Floor cloud transports at 300s.
if timeout < 300 {
timeout = 300
}
t := NewOpenAICompatTransportWith(baseURL, apiKey, providerName, timeout, contextWindow)
return t, nil
}
+47
View File
@@ -0,0 +1,47 @@
// phaseb.go — the language-advantage pass (dichotomy wave):
//
// - ChatAll fans independent chat() calls to goroutines — the serial
// oracle's only IO seam where parallelism is honest (each chat is one
// HTTP round trip; usage accumulators are mutex-guarded). The frozen
// battery replays bytes through the parallel path.
// - Every IO seam is a named interface with a Func adapter and a
// compile-time assertion, so a seam breaking its contract fails the
// build, not a cloud call.
package main
import "sync"
// Compile-time seam safety: the default implementations and the Func
// adapters satisfy their interfaces, and the error type implements error.
// A seam drifting from its contract fails the build, not a cloud call.
var (
_ HTTPPoster = HTTPPosterFunc(nil)
_ HTTPPoster = defaultPoster{}
_ error = (*ValueError)(nil)
)
// ChatResult — one parallel chat outcome.
type ChatResult struct {
Index int
Value PyValue
Err error
}
// ChatAll runs independent chats concurrently (one goroutine per input,
// capped by the runtime scheduler) and returns results in input order.
// Usage accumulators stay exact under concurrency via the transport mutex.
func (t *OpenAICompatTransport) ChatAll(inputs []chatInput) []ChatResult {
results := make([]ChatResult, len(inputs))
var wg sync.WaitGroup
for i := range inputs {
wg.Add(1)
go func(i int) {
defer wg.Done()
v, err := t.chat(inputs[i])
results[i] = ChatResult{Index: i, Value: v, Err: err}
}(i)
}
wg.Wait()
return results
}
@@ -0,0 +1,93 @@
// Phase B — the language-advantage pass. ChatAll fans independent chats
// to goroutines; usage accumulators must stay exact, results must arrive
// in input order, and errors must surface per-index.
package main
import (
"strings"
"sync"
"testing"
)
func TestPhaseBParallelChatOrderAndUsage(t *testing.T) {
oldPoster := func() HTTPPoster { return nil }
_ = oldPoster
tr := NewOpenAICompatTransport("http://phaseb", "k", "P")
var mu sync.Mutex
n := 0
tr.SetPoster(HTTPPosterFunc(func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
mu.Lock()
n++
mu.Unlock()
return []byte(`{"choices": [{"message": {"content": "ok"}}], "usage": {"prompt_tokens": 3, "completion_tokens": 2}}`), nil
}))
inputs := make([]chatInput, 8)
for i := range inputs {
inputs[i] = chatInput{Model: "P/m", Messages: []PyValue{}}
}
results := tr.ChatAll(inputs)
for i, r := range results {
if r.Err != nil {
t.Fatalf("index %d: %v", i, r.Err)
}
if r.Index != i {
t.Fatalf("index %d out of order: %+v", i, r)
}
o := mustObj(r.Value)
if c, _ := o.Get("content"); c != "ok" {
t.Fatalf("index %d content: %v", i, c)
}
}
if tr.usageCalls != 8 || tr.usagePromptTokens != 24 || tr.usageOutputTokens != 16 {
t.Fatalf("usage drift: %+v", tr)
}
if tr.usageTotalDurationNS <= 0 {
t.Fatal("duration not accumulated under parallel chats")
}
if n != 8 {
t.Fatalf("poster calls: got %d want 8", n)
}
}
func TestPhaseBHammer(t *testing.T) {
tr := NewOpenAICompatTransport("http://phaseb", "k", "P")
tr.SetPoster(HTTPPosterFunc(func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
return []byte(`{"choices": [{"message": {"content": "x"}}]}`), nil
}))
inputs := make([]chatInput, 32)
for i := range inputs {
inputs[i] = chatInput{Model: "P/m", Messages: []PyValue{}}
}
results := tr.ChatAll(inputs)
for i, r := range results {
if r.Err != nil {
t.Fatalf("index %d: %v", i, r.Err)
}
}
if tr.usageCalls != 32 {
t.Fatalf("usage calls: got %d want 32", tr.usageCalls)
}
}
func TestPhaseBParallelErrorsPerIndex(t *testing.T) {
tr := NewOpenAICompatTransport("http://phaseb", "k", "P")
tr.SetPoster(HTTPPosterFunc(func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
return []byte(`[]`), nil
}))
inputs := []chatInput{
{Model: "P/m", Messages: []PyValue{}},
{Model: "P/m", Messages: []PyValue{}},
}
results := tr.ChatAll(inputs)
for i, r := range results {
if r.Err == nil {
t.Fatalf("index %d: expected error", i)
}
if !strings.Contains(r.Err.Error(), "non-object response") {
t.Fatalf("index %d: %v", i, r.Err)
}
}
if tr.usageCalls != 0 {
t.Fatalf("failed chats must not accumulate: %+v", tr)
}
}
+396
View File
@@ -0,0 +1,396 @@
// pyjson.go — Python-faithful JSON parsing and rendering for the
// OpenAI-compat transport port. The oracle round-trips wire payloads through
// json.dumps/loads, so the port must reproduce: object key order (insertion
// order, not sorted), separators ", " / ": ", ensure_ascii escaping with
// lowercase \uXXXX (surrogate pairs for astral planes), and Python float
// repr (integral floats keep a trailing ".0" — json.dumps(2.0) is "2.0").
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
)
// PyValue — a JSON value with order-preserving objects.
// nil | bool | json.Number | string | []PyValue | *PyObj
type PyValue any
// PyObj — a JSON object preserving insertion order.
type PyObj struct {
Keys []string
Vals map[string]PyValue
}
func NewPyObj() *PyObj { return &PyObj{Vals: map[string]PyValue{}} }
func (o *PyObj) Get(key string) (PyValue, bool) {
if o == nil {
return nil, false
}
v, ok := o.Vals[key]
return v, ok
}
func (o *PyObj) Set(key string, v PyValue) {
if _, dup := o.Vals[key]; !dup {
o.Keys = append(o.Keys, key)
}
o.Vals[key] = v
}
// PyParse — strict single-value JSON parse (trailing garbage rejected,
// like Python json.loads).
func PyParse(raw []byte) (PyValue, error) {
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
v, err := decodeValue(dec)
if err != nil {
return nil, err
}
if _, err := dec.Token(); err != io.EOF {
return nil, fmt.Errorf("trailing data after JSON value")
}
return v, nil
}
func decodeValue(dec *json.Decoder) (PyValue, error) {
tok, err := dec.Token()
if err != nil {
return nil, err
}
return decodeFromToken(dec, tok)
}
func decodeFromToken(dec *json.Decoder, tok json.Token) (PyValue, error) {
switch t := tok.(type) {
case json.Delim:
switch t {
case '{':
obj := NewPyObj()
for dec.More() {
keyTok, err := dec.Token()
if err != nil {
return nil, err
}
key, ok := keyTok.(string)
if !ok {
return nil, fmt.Errorf("object key is not a string")
}
val, err := decodeValue(dec)
if err != nil {
return nil, err
}
obj.Set(key, val)
}
if _, err := dec.Token(); err != nil { // closing '}'
return nil, err
}
return obj, nil
case '[':
arr := []PyValue{}
for dec.More() {
val, err := decodeValue(dec)
if err != nil {
return nil, err
}
arr = append(arr, val)
}
if _, err := dec.Token(); err != nil { // closing ']'
return nil, err
}
return arr, nil
default:
return nil, fmt.Errorf("unexpected delim %q", t)
}
case json.Number:
return t, nil
case string:
return t, nil
case bool:
return t, nil
case nil:
return nil, nil
default:
return nil, fmt.Errorf("unexpected token %v", tok)
}
}
// PyDumpsCompact — Python json.dumps(value): separators ", " / ": ",
// ensure_ascii=True, insertion-order objects.
func PyDumpsCompact(v PyValue) string {
var b strings.Builder
writePy(&b, v)
return b.String()
}
func writePy(b *strings.Builder, v PyValue) {
switch t := v.(type) {
case nil:
b.WriteString("null")
case bool:
if t {
b.WriteString("true")
} else {
b.WriteString("false")
}
case json.Number:
b.WriteString(PyNumStr(t))
case string:
writePyString(b, t)
case []PyValue:
b.WriteByte('[')
for i, item := range t {
if i > 0 {
b.WriteString(", ")
}
writePy(b, item)
}
b.WriteByte(']')
case *PyObj:
b.WriteByte('{')
for i, k := range t.Keys {
if i > 0 {
b.WriteString(", ")
}
writePyString(b, k)
b.WriteString(": ")
writePy(b, t.Vals[k])
}
b.WriteByte('}')
default:
b.WriteString(fmt.Sprintf("%v", v))
}
}
// PyNumStr — Python json.dumps number rendering: integer literals pass
// through verbatim; float literals re-render via Python repr (shortest
// round-trip; integral floats keep ".0"). Fixture numbers are written by
// json.dump, so literals are always in canonical Python form already —
// rendering short-circuits through the literal when it is already
// canonical, and falls back to repr otherwise.
func PyNumStr(n json.Number) string {
s := n.String()
if pyNumCanonical(s) {
return s
}
f, err := n.Float64()
if err != nil {
return s
}
return pyFloatRepr(f)
}
func pyNumCanonical(s string) bool {
if s == "" {
return false
}
hasDot := false
for i := 0; i < len(s); i++ {
c := s[i]
switch {
case c >= '0' && c <= '9':
case c == '.':
if hasDot {
return false
}
hasDot = true
case c == 'e' || c == 'E' || c == '+' || c == '-':
if c == '-' && i == 0 {
continue
}
return false
default:
return false
}
}
return true
}
// pyFloatRepr — Python repr() of a float: shortest round-trip, integral
// values keep ".0". Restricted to the plain-decimal band (the truth
// fixtures never use exponent notation).
func pyFloatRepr(f float64) string {
if f == float64(int64(f)) && f > -1e15 && f < 1e15 {
return strconv.FormatInt(int64(f), 10) + ".0"
}
return strconv.FormatFloat(f, 'g', -1, 64)
}
// writePyString — Python json.dumps string escaping, ensure_ascii=True.
func writePyString(b *strings.Builder, s string) {
b.WriteByte('"')
for _, c := range s {
switch c {
case '"':
b.WriteString("\\\"")
case '\\':
b.WriteString("\\\\")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
case '\b':
b.WriteString("\\b")
case '\f':
b.WriteString("\\f")
default:
if c < 0x20 {
fmt.Fprintf(b, "\\u%04x", c)
} else if c <= 0x7E {
b.WriteRune(c)
} else if cp := uint32(c); cp > 0xFFFF {
v := cp - 0x10000
fmt.Fprintf(b, "\\u%04x\\u%04x", 0xD800+(v>>10), 0xDC00+(v&0x3FF))
} else {
fmt.Fprintf(b, "\\u%04x", cp)
}
}
}
b.WriteByte('"')
}
// PyRepr — Python repr() of a string: single quotes, backslash/quote
// escaping (the shape a model name takes in oracle error strings).
func PyRepr(s string) string {
var b strings.Builder
b.WriteByte('\'')
for _, c := range s {
switch c {
case '\\':
b.WriteString("\\\\")
case '\'':
b.WriteString("\\'")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
default:
b.WriteRune(c)
}
}
b.WriteByte('\'')
return b.String()
}
// PyStr — Python str() of a JSON value for the non-dict arguments branch
// of _to_openai_messages (str(args) when args is not a dict).
func PyStr(v PyValue) string {
switch t := v.(type) {
case nil:
return "None"
case bool:
if t {
return "True"
}
return "False"
case json.Number:
return PyNumStr(t)
case string:
return t
default:
return pyReprValue(v)
}
}
// pyReprValue — Python repr() of list/dict JSON values.
func pyReprValue(v PyValue) string {
switch t := v.(type) {
case string:
return PyRepr(t)
case []PyValue:
parts := make([]string, len(t))
for i, item := range t {
parts[i] = pyReprValue(item)
}
return "[" + strings.Join(parts, ", ") + "]"
case *PyObj:
parts := make([]string, 0, len(t.Keys))
for _, k := range t.Keys {
parts = append(parts, PyRepr(k)+": "+pyReprValue(t.Vals[k]))
}
return "{" + strings.Join(parts, ", ") + "}"
default:
return PyStr(v)
}
}
// PyTruthy — Python truthiness of a JSON value.
func PyTruthy(v PyValue) bool {
switch t := v.(type) {
case nil:
return false
case bool:
return t
case json.Number:
f, err := t.Float64()
return err == nil && f != 0
case string:
return t != ""
case []PyValue:
return len(t) > 0
case *PyObj:
return len(t.Keys) > 0
default:
return true
}
}
// PyEqual — deep equality where numbers compare numerically (int 120 ==
// float 120.0), matching semantic oracle equality rather than literal text.
func PyEqual(a, b PyValue) bool {
an, aok := a.(json.Number)
bn, bok := b.(json.Number)
if aok && bok {
if an.String() == bn.String() {
return true
}
af, aerr := an.Float64()
bf, berr := bn.Float64()
return aerr == nil && berr == nil && af == bf
}
switch at := a.(type) {
case nil:
return b == nil
case bool:
bt, ok := b.(bool)
return ok && at == bt
case string:
bt, ok := b.(string)
return ok && at == bt
case []PyValue:
bt, ok := b.([]PyValue)
if !ok || len(at) != len(bt) {
return false
}
for i := range at {
if !PyEqual(at[i], bt[i]) {
return false
}
}
return true
case *PyObj:
bt, ok := b.(*PyObj)
if !ok || len(at.Keys) != len(bt.Keys) {
return false
}
for i, k := range at.Keys {
if bt.Keys[i] != k {
return false
}
if !PyEqual(at.Vals[k], bt.Vals[k]) {
return false
}
}
return true
default:
return false
}
}
+490
View File
@@ -0,0 +1,490 @@
// Replay — the frozen-truth parity engine. Reads truth.json (generated by
// scripts/gen_openai_compat_transport_truth.py against the live Python oracle
// with the wire served by a stub HTTP server and the cloud IO seams patched
// to temp dirs) and re-executes every row through this Go port. @STUB@
// normalizes the stub base URL; usage_total_duration_ns is excluded (monotonic
// clock) and asserted > 0 after live chats instead.
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
)
// TruthDoc — the frozen truth file structure.
type TruthDoc struct {
Oracle string `json:"oracle"`
Generator string `json:"generator"`
Note string `json:"note"`
Rows []TruthRow `json:"rows"`
}
// TruthRow — one frozen case.
type TruthRow struct {
Name string `json:"name"`
Op string `json:"op"`
Setup json.RawMessage `json:"setup"`
Want json.RawMessage `json:"want"`
}
// RunTruth replays every row and returns a divergence report (empty on
// 100% parity).
func RunTruth(path string) []string {
raw, err := os.ReadFile(path)
if err != nil {
return []string{fmt.Sprintf("read truth: %v", err)}
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return []string{fmt.Sprintf("parse truth: %v", err)}
}
var fails []string
for i := range doc.Rows {
if msg := replayRow(&doc.Rows[i]); msg != "" {
fails = append(fails, doc.Rows[i].Name+": "+msg)
}
}
return fails
}
func truthRowCount() int {
raw, err := os.ReadFile("truth.json")
if err != nil {
return -1
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return -1
}
return len(doc.Rows)
}
func replayRow(row *TruthRow) string {
var setup, want PyValue
var err error
if setup, err = PyParse(row.Setup); err != nil {
return fmt.Sprintf("parse setup: %v", err)
}
if want, err = PyParse(row.Want); err != nil {
return fmt.Sprintf("parse want: %v", err)
}
s := mustObj(setup)
w := want
switch row.Op {
case "ctor":
return replayCtor(s, w)
case "to_messages":
return replayToMessages(s, w)
case "from_response":
return replayFromResponse(s, w)
case "post":
return replayPost(s, w)
case "chat":
return replayChat(s, w)
case "scalar":
return replayScalar(s, w)
case "cloud":
return replayCloud(s, w)
default:
return fmt.Sprintf("unknown op %q", row.Op)
}
}
func mustObj(v PyValue) *PyObj {
o, _ := v.(*PyObj)
if o == nil {
return NewPyObj()
}
return o
}
func strOf(v PyValue) string {
s, _ := v.(string)
return s
}
func numOf(v PyValue) float64 {
if n, ok := v.(json.Number); ok {
f, _ := n.Float64()
return f
}
return 0
}
func listOf(v PyValue) []PyValue {
l, _ := v.([]PyValue)
if l == nil {
return []PyValue{}
}
return l
}
// -- ctor --------------------------------------------------------------------
func replayCtor(s *PyObj, w PyValue) string {
base, _ := s.Get("base_url")
key, _ := s.Get("api_key")
prov, _ := s.Get("provider")
timeout := 120.0
if tv, present := s.Get("timeout"); present {
timeout = numOf(tv)
}
ctxWin := 131072
if cv, present := s.Get("context_window"); present {
ctxWin = int(numOf(cv))
}
t := NewOpenAICompatTransportWith(strOf(base), strOf(key), strOf(prov), timeout, ctxWin)
got := transportAttrs(t)
if msg := compareJSON("attrs", got, w); msg != "" {
return msg
}
return ""
}
func transportAttrs(t *OpenAICompatTransport) PyValue {
o := NewPyObj()
o.Set("base_url", t.BaseURL)
o.Set("api_key", t.APIKey)
o.Set("provider", t.Provider)
o.Set("timeout", json.Number(fmt.Sprintf("%v", t.Timeout)))
o.Set("context_window", json.Number(fmt.Sprintf("%d", t.contextWindow)))
o.Set("usage_calls", json.Number(fmt.Sprintf("%d", t.usageCalls)))
o.Set("usage_prompt_tokens", json.Number(fmt.Sprintf("%d", t.usagePromptTokens)))
o.Set("usage_output_tokens", json.Number(fmt.Sprintf("%d", t.usageOutputTokens)))
o.Set("usage_total_duration_ns", json.Number(fmt.Sprintf("%d", t.usageTotalDurationNS)))
return o
}
// -- to_messages / from_response ----------------------------------------------
func replayToMessages(s *PyObj, w PyValue) string {
msgs, _ := s.Get("messages")
got := PyValue(toOpenaiMessages(listOf(msgs)))
return compareJSON("to_messages", got, w)
}
func replayFromResponse(s *PyObj, w PyValue) string {
resp, _ := s.Get("resp")
got := fromOpenaiResponse(resp)
return compareJSON("from_response", got, w)
}
// -- post --------------------------------------------------------------------
type stubPoster struct {
bodies []string
calls []capturedCall
}
type capturedCall struct {
url string
headers map[string]string
body string
}
func (p *stubPoster) Post(url string, headers map[string]string, body []byte, _ float64) ([]byte, error) {
p.calls = append(p.calls, capturedCall{url, headers, string(body)})
resp := p.bodies[0]
p.bodies = p.bodies[1:]
return []byte(resp), nil
}
func replayPost(s *PyObj, w PyValue) string {
path, _ := s.Get("path")
payload, _ := s.Get("payload")
bodiesRaw, _ := s.Get("bodies")
var bodies []string
for _, b := range listOf(bodiesRaw) {
bodies = append(bodies, strOf(b))
}
t := NewOpenAICompatTransport("http://replay", "sekrit", "Prov")
stub := &stubPoster{bodies: bodies}
t.SetPoster(stub)
var got PyValue
result, err := t.wirePost(strOf(path), payload)
if err != nil {
eo := NewPyObj()
eo.Set("error", err.Error())
got = PyValue(eo)
} else {
got = result
}
call := stub.calls[0]
reqObj := NewPyObj()
reqObj.Set("path", strings.TrimPrefix(call.url, "http://replay"))
reqObj.Set("authorization", call.headers["Authorization"])
reqObj.Set("content_type", call.headers["Content-Type"])
reqObj.Set("body", call.body)
wo := mustObj(w)
if msg := compareJSON("request", reqObj, mustGet(wo, "request")); msg != "" {
return msg
}
if _, wantErr := wo.Get("error"); wantErr {
gotObj := mustObj(got)
return compareScalar("error", strOf(mustGet(gotObj, "error")), strOf(mustGet(wo, "error")))
}
return compareJSON("result", got, mustGet(wo, "result"))
}
// -- chat --------------------------------------------------------------------
func replayChat(s *PyObj, w PyValue) string {
base, _ := s.Get("base_url")
key, _ := s.Get("api_key")
prov, _ := s.Get("provider")
model, _ := s.Get("model")
msgs, _ := s.Get("messages")
tools, toolsPresent := s.Get("tools")
options, _ := s.Get("options")
think, thinkPresent := s.Get("think")
respRaw, _ := s.Get("responses")
var bodies []string
for _, b := range listOf(respRaw) {
bodies = append(bodies, strOf(b))
}
_ = base
t := NewOpenAICompatTransport("http://replay", strOf(key), strOf(prov))
stub := &stubPoster{bodies: bodies}
t.SetPoster(stub)
var in chatInput
in.Model = strOf(model)
in.Messages = listOf(msgs)
if toolsPresent {
in.Tools = tools
}
if oo, ok := options.(*PyObj); ok {
in.Options = oo
}
if thinkPresent {
in.Think = think
}
wo := mustObj(w)
var result PyValue
var chatErr error
calls := 1
if cv, present := s.Get("calls"); present {
calls = int(numOf(cv))
}
for c := 0; c < calls; c++ {
result, chatErr = t.chat(in)
}
var gotResult PyValue
if chatErr != nil {
eo := NewPyObj()
eo.Set("error", chatErr.Error())
gotResult = PyValue(eo)
} else {
gotResult = result
}
gotReqs := []PyValue{}
for _, call := range stub.calls {
body, perr := PyParse([]byte(call.body))
if perr != nil {
return fmt.Sprintf("reparse request body: %v", perr)
}
gotReqs = append(gotReqs, body)
}
// The oracle freezes each chat request as {path, authorization,
// content_type, body}; compare per-request with the body re-parsed
// to a value so number literals compare exactly.
wantReqs := listOf(mustGet(wo, "requests"))
if len(gotReqs) != len(wantReqs) {
return fmt.Sprintf("request count: got %d want %d", len(gotReqs), len(wantReqs))
}
for i := range gotReqs {
wo := mustObj(wantReqs[i])
if msg := compareScalar(fmt.Sprintf("request[%d].path", i), "/chat/completions", strOf(mustGet(wo, "path"))); msg != "" {
return msg
}
if msg := compareScalar(fmt.Sprintf("request[%d].auth", i), "Bearer "+strOf(key), strOf(mustGet(wo, "authorization"))); msg != "" {
return msg
}
if msg := compareScalar(fmt.Sprintf("request[%d].ctype", i), "application/json", strOf(mustGet(wo, "content_type"))); msg != "" {
return msg
}
wantBody, perr := PyParse([]byte(strOf(mustGet(wo, "body"))))
if perr != nil {
return fmt.Sprintf("reparse want body: %v", perr)
}
if msg := compareJSON(fmt.Sprintf("request[%d].body", i), gotReqs[i], wantBody); msg != "" {
return msg
}
}
if _, wantErr := wo.Get("error"); wantErr {
gotObj := mustObj(gotResult)
if msg := compareScalar("error", strOf(mustGet(gotObj, "error")), strOf(mustGet(wo, "error"))); msg != "" {
return msg
}
} else if msg := compareJSON("result", gotResult, mustGet(wo, "result")); msg != "" {
return msg
}
usage := mustObj(mustGet(wo, "usage"))
if msg := compareScalar("usage.calls", itoa(t.usageCalls), numStr(mustGet(usage, "calls"))); msg != "" {
return msg
}
if msg := compareScalar("usage.prompt", itoa(t.usagePromptTokens), numStr(mustGet(usage, "prompt_tokens"))); msg != "" {
return msg
}
if msg := compareScalar("usage.output", itoa(t.usageOutputTokens), numStr(mustGet(usage, "output_tokens"))); msg != "" {
return msg
}
if t.usageTotalDurationNS <= 0 && chatErr == nil {
return "usage_total_duration_ns not accumulated"
}
return ""
}
// -- scalar ------------------------------------------------------------------
func replayScalar(s *PyObj, w PyValue) string {
fn, _ := s.Get("fn")
model, _ := s.Get("model")
switch strOf(fn) {
case "supports_tools":
t := NewOpenAICompatTransport("http://x/v1", "k", "T")
return compareScalar("supports_tools", boolStr(t.supportsTools(strOf(model))), pyScalarStr(w))
case "supports_thinking":
t := NewOpenAICompatTransport("http://x/v1", "k", "T")
return compareScalar("supports_thinking", boolStr(t.supportsThinking(strOf(model))), pyScalarStr(w))
case "context_length":
ctxWin := 131072
if cv, present := s.Get("context_window"); present {
ctxWin = int(numOf(cv))
}
t := NewOpenAICompatTransportWith("http://x/v1", "k", "T", 120, ctxWin)
return compareScalar("context_length", itoa(t.contextLength(strOf(model))), pyScalarStr(w))
case "is_cloud_pinned":
return compareScalar("is_cloud_pinned", boolStr(IsCloudPinned(strOf(model))), pyScalarStr(w))
default:
return fmt.Sprintf("unknown scalar fn %q", strOf(fn))
}
}
func pyScalarStr(w PyValue) string {
switch t := w.(type) {
case bool:
return boolStr(t)
case json.Number:
return t.String()
case string:
return t
default:
return fmt.Sprintf("%v", w)
}
}
// -- cloud -------------------------------------------------------------------
func replayCloud(s *PyObj, w PyValue) string {
model, _ := s.Get("model")
timeout := 120.0
if tv, present := s.Get("timeout"); present {
timeout = numOf(tv)
}
envRaw, _ := s.Get("env")
env := map[string]string{}
if eo, ok := envRaw.(*PyObj); ok {
for _, k := range eo.Keys {
env[k] = strOf(eo.Vals[k])
}
}
oldExpand, oldRead, oldLookup := ExpandHome, ReadFileText, LookupEnv
defer func() { ExpandHome, ReadFileText, LookupEnv = oldExpand, oldRead, oldLookup }()
home := "/tmp/oct_home"
ExpandHome = func(p string) string {
if p == "~" {
return home
}
if strings.HasPrefix(p, "~/") {
return home + p[1:]
}
return p
}
// The oracle patches os.path.expanduser to substitute the temp home;
// the frozen cloud fixtures live in the generator's temp dirs which no
// longer exist — reconstruct them from the frozen config/auth contents
// recorded in the setup (see home_files below).
files := map[string]string{}
if hf, present := s.Get("home_files"); present {
if ho, ok := hf.(*PyObj); ok {
for _, k := range ho.Keys {
files[home+k] = strOf(ho.Vals[k])
}
}
}
ReadFileText = func(path string) (string, bool, error) {
if text, ok := files[path]; ok {
return text, true, nil
}
return "", false, nil
}
LookupEnv = func(key string) (string, bool) {
v, ok := env[key]
return v, ok
}
t, err := CloudTransportFor(strOf(model), timeout)
wo := mustObj(w)
if _, wantErr := wo.Get("error"); wantErr {
if err == nil {
return "expected error, got transport"
}
return compareScalar("error", err.Error(), strOf(mustGet(wo, "error")))
}
if err != nil {
return fmt.Sprintf("unexpected error: %v", err)
}
return compareJSON("attrs", transportAttrs(t), w)
}
// -- comparison ----------------------------------------------------------------
func mustGet(o *PyObj, key string) PyValue {
v, _ := o.Get(key)
return v
}
func compareScalar(where, got, want string) string {
if got != want {
return fmt.Sprintf("%s: got %q want %q", where, got, want)
}
return ""
}
func compareJSON(where string, got, want PyValue) string {
if !PyEqual(canonicalJSON(got), canonicalJSON(want)) {
return fmt.Sprintf("%s: got %s want %s", where, PyDumpsCompact(got), PyDumpsCompact(want))
}
return ""
}
// canonicalJSON re-parses both sides through the literal-preserving parser
// so number comparison is literal-exact (PyEqual adds numeric tolerance).
func canonicalJSON(v PyValue) PyValue {
re, err := PyParse([]byte(PyDumpsCompact(v)))
if err != nil {
return v
}
return re
}
func boolStr(b bool) string {
if b {
return "true"
}
return "false"
}
// numStr — oracle usage ints arrive as JSON numbers.
func numStr(v PyValue) string {
if n, ok := v.(json.Number); ok {
return n.String()
}
return strOf(v)
}
func itoa(n int) string { return fmt.Sprintf("%d", n) }
var _ = reflect.TypeOf
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,91 @@
// Battery — Go tests replaying the frozen oracle truth VERBATIM.
// Every row of truth.json (generated by
// scripts/gen_openai_compat_transport_truth.py against the live Python
// oracle) re-executes through this port; any divergence fails the test.
package main
import (
"encoding/json"
"os"
"testing"
)
func loadTruth(t *testing.T) TruthDoc {
t.Helper()
raw, err := os.ReadFile("truth.json")
if err != nil {
t.Fatalf("read truth: %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth: %v", err)
}
if len(doc.Rows) == 0 {
t.Fatal("truth has no rows")
}
return doc
}
func TestBatteryRowCount(t *testing.T) {
doc := loadTruth(t)
if len(doc.Rows) != 83 {
t.Fatalf("row count drift: got %d want 83", len(doc.Rows))
}
if doc.Oracle != "src/rune/openai_compat_transport.py" {
t.Fatalf("oracle drift: %q", doc.Oracle)
}
}
func TestBatteryParity(t *testing.T) {
doc := loadTruth(t)
for i := range doc.Rows {
row := &doc.Rows[i]
if msg := replayRow(row); msg != "" {
t.Errorf("%s: %s", row.Name, msg)
}
}
}
func TestBatteryByOp(t *testing.T) {
doc := loadTruth(t)
byOp := map[string][]*TruthRow{}
for i := range doc.Rows {
byOp[doc.Rows[i].Op] = append(byOp[doc.Rows[i].Op], &doc.Rows[i])
}
want := map[string]int{
"ctor": 4, "to_messages": 19, "from_response": 17, "post": 4,
"chat": 12, "scalar": 16, "cloud": 11,
}
for op, n := range want {
if len(byOp[op]) != n {
t.Errorf("op %s: got %d rows want %d", op, len(byOp[op]), n)
}
}
for op, rows := range byOp {
t.Run(op, func(t *testing.T) {
for _, row := range rows {
if msg := replayRow(row); msg != "" {
t.Errorf("%s: %s", row.Name, msg)
}
}
})
}
}
func TestBatteryDeterministic(t *testing.T) {
raw, err := os.ReadFile("truth.json")
if err != nil {
t.Fatalf("read truth: %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth: %v", err)
}
names := map[string]bool{}
for _, row := range doc.Rows {
if names[row.Name] {
t.Fatalf("duplicate row name %q", row.Name)
}
names[row.Name] = true
}
}