feat(voice_injector): literal-100% residue — dichotomy wave
Port the two audit-missing fns name-preserving into the Go impl (gaps.go) + the REAL inject_voice logic the shape-check replay never carried (injectVoice): * toDict — the VoiceInjection.to_dict insertion-order contract (dimensions, guidance, token_count) as ordered KV pairs, plus toDictJSON: the BYTE law json.dumps(to_dict(), indent=2) exactly — ensure_ascii \uXXXX escapes, the five short escapes, integral-float rule (emphasis 1.0 stays "1.0"), Go-map-sorting sidestepped. * testInjection — the oracle's 5-fixed-message test report; inject is the seam (io.Writer + func). The >100-rune unicode essence proves guidance[:100] slices by CODEPOINT (truncateRunes — sleep_time trap). * injectVoice — the blend peeled to an argument (task_gate boundary law: voice_hybrid.blend is DB-backed reactive Python): keep emphasis >= 0.15 (boundary 0.15 kept, 0.149/0.05 dropped), join "- name: essence" lines with \n, estimate tokens on the guidance. Truth: gen_voice_injector_truth.py stubs the oracle's voice_hybrid symbol with a deterministic frozen-blend table and freezes 13 cases (4 estimate regression incl. rune-count, 5 inject with boundary/ integral/unicode/empty blends, 3 to_dict incl. empty + integral emphasis byte law, 1 test_injection full stdout). Regen x2 byte-identical (md5 evidence). Battery TestGapsParity 13/13 green; go test + vet + gofmt + build clean. Voice-family pytest 21/21 BOTH pins (live kernel + RUNE_VOICE_HYBRID_CORE=python; the vh .so built in-checkout first — gitignored artifact). voice_injector 3/5 → 5/5 fn-level FULL.
This commit is contained in:
1 parent
a399477bff
commit
d268b365ac
11 files changed
+814
No files matched your search
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_voice_injector_truth.py — freeze/replay parity truth for
|
||||
voice_injector (literal-100% residue lane, dichotomy wave #2479).
|
||||
|
||||
Freeze mode (default): exercises the LIVE Python oracle
|
||||
(src/rune/voice_injector.py) across every branch of the audit-missing
|
||||
fns (to_dict, test_injection) plus the real inject_voice logic and the
|
||||
estimate_tokens regression, recording rows into
|
||||
src/go/voice-injector/truth_gaps.json.
|
||||
|
||||
Rig law: voice_hybrid.blend is DB-backed (reactive) — the oracle's
|
||||
imported `voice_hybrid` symbol is replaced with a stub whose blend(msg)
|
||||
returns per-message FROZEN blends. Every inject case records both the
|
||||
blend INPUT (the seam payload) and the oracle's expected OUTPUT, so the
|
||||
Go kernel replays injectVoice(message, blend) deterministically. The
|
||||
real blend stays on the Python side (task_gate boundary law).
|
||||
|
||||
to_dict byte law: each row carries want_json = json.dumps(to_dict(),
|
||||
indent=2) — the exact bytes the oracle's `inject` CLI prints (insertion
|
||||
order + ensure_ascii + integral-float rule).
|
||||
"""
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
TRUTH_PATH = REPO / "src" / "go" / "voice-injector" / "truth_gaps.json"
|
||||
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import voice_injector as oracle_mod # noqa: E402
|
||||
|
||||
|
||||
class _BlendStub:
|
||||
"""Deterministic voice_hybrid stand-in: blend(msg) -> frozen table."""
|
||||
|
||||
def __init__(self, table):
|
||||
self.table = table
|
||||
|
||||
def blend(self, message):
|
||||
return json.loads(json.dumps(self.table[message]))
|
||||
|
||||
|
||||
FIXED_MESSAGES = [
|
||||
"hello",
|
||||
"fix the failing test",
|
||||
"what do you think about capitalism?",
|
||||
"I'm feeling frustrated with this code",
|
||||
"can you explain how this works?",
|
||||
]
|
||||
|
||||
# Frozen blends per fixed message: threshold boundary (0.15 kept,
|
||||
# 0.149 dropped), integral emphasis 1.0 (float-render law), unicode
|
||||
# essences (rune laws), empty-dimension messages, a >100-char unicode
|
||||
# essence proving guidance[:100] slices by CODEPOINT.
|
||||
BLEND_TABLE = {
|
||||
"hello": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{"name": "Warmth", "emphasis": 0.15,
|
||||
"essence": "speak like a friend"},
|
||||
],
|
||||
"guidance": ["- Warmth: speak like a friend"],
|
||||
},
|
||||
"fix the failing test": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{"name": "Pragmatism", "emphasis": 0.8,
|
||||
"essence": "ship the fix, then the theory"},
|
||||
{"name": "Warmth", "emphasis": 0.05,
|
||||
"essence": "dropped below threshold"},
|
||||
{"name": "Curiosity", "emphasis": 0.149,
|
||||
"essence": "dropped just below boundary"},
|
||||
{"name": "Rigor", "emphasis": 1.0,
|
||||
"essence": "prove it or it didn't happen"},
|
||||
],
|
||||
"guidance": [],
|
||||
},
|
||||
"what do you think about capitalism?": {
|
||||
"voice": "unified",
|
||||
"dimensions": [],
|
||||
"guidance": [],
|
||||
},
|
||||
"I'm feeling frustrated with this code": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{"name": "Honnêteté", "emphasis": 0.5,
|
||||
"essence": "dis ça direct, avec bienveillance"},
|
||||
{"name": "Warmth", "emphasis": 0.25,
|
||||
"essence": "l'utilisateur est frustré — reste calme ✓"},
|
||||
{"name": "Pragmatism", "emphasis": 0.1499,
|
||||
"essence": "just under the line"},
|
||||
],
|
||||
"guidance": [],
|
||||
},
|
||||
"can you explain how this works?": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{"name": "Pédagogie", "emphasis": 0.9,
|
||||
"essence": ("pars du concret, monte vers l'abstraction "
|
||||
"avec un exemple à chaque marche — " + "é" * 80)},
|
||||
],
|
||||
"guidance": [],
|
||||
},
|
||||
}
|
||||
|
||||
oracle_mod.voice_hybrid = _BlendStub(BLEND_TABLE)
|
||||
|
||||
|
||||
def main():
|
||||
cases = []
|
||||
|
||||
# estimate_tokens regression (rune-count law, ASCII + unicode)
|
||||
for text, tag in [
|
||||
("hello world test", "ascii"),
|
||||
("héllo wörld ✓✓✓✓", "unicode_runes"),
|
||||
("", "empty"),
|
||||
("ab", "under_four"),
|
||||
]:
|
||||
cases.append({"op": "estimate", "tag": tag, "text": text,
|
||||
"want": oracle_mod.estimate_tokens(text)})
|
||||
|
||||
# inject_voice: blend input (the seam payload) + oracle output
|
||||
for msg in FIXED_MESSAGES:
|
||||
inj = oracle_mod.inject_voice(msg)
|
||||
d = inj.to_dict()
|
||||
cases.append({
|
||||
"op": "inject", "tag": msg[:24], "message": msg,
|
||||
"blend": BLEND_TABLE[msg],
|
||||
"want": d,
|
||||
"want_json": json.dumps(d, indent=2),
|
||||
})
|
||||
|
||||
# to_dict directly: constructed injections incl. empty + unicode
|
||||
for dims, guidance, tokens, tag in [
|
||||
([{"name": "Warmth", "emphasis": 0.15, "essence": "calme ✓"}],
|
||||
"- Warmth: calme ✓", 3, "unicode_minimal"),
|
||||
([], "", 0, "empty"),
|
||||
([{"name": "A", "emphasis": 1.0, "essence": "x"},
|
||||
{"name": "B", "emphasis": 0.2, "essence": "y"}],
|
||||
"- A: x\n- B: y", 4, "integral_emphasis"),
|
||||
]:
|
||||
vi = oracle_mod.VoiceInjection(
|
||||
dimensions=json.loads(json.dumps(dims)),
|
||||
guidance=guidance, token_count=tokens)
|
||||
d = vi.to_dict()
|
||||
cases.append({
|
||||
"op": "to_dict", "tag": tag,
|
||||
"injection": {"dimensions": dims, "guidance": guidance,
|
||||
"token_count": tokens},
|
||||
"want": d,
|
||||
"want_json": json.dumps(d, indent=2),
|
||||
})
|
||||
|
||||
# test_injection: the full stdout law under the frozen blends
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
oracle_mod.test_injection()
|
||||
cases.append({"op": "test_injection", "tag": "stdout_law",
|
||||
"blends": BLEND_TABLE, "want_stdout": out.getvalue()})
|
||||
|
||||
TRUTH_PATH.write_text(
|
||||
json.dumps(cases, indent=1, ensure_ascii=False) + "\n",
|
||||
encoding="utf-8")
|
||||
print(f"wrote {TRUTH_PATH} ({len(cases)} cases)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,179 @@
|
||||
package main
|
||||
|
||||
// gaps.go — literal-100% residue (dichotomy wave #2479): the two
|
||||
// audit-missing oracle surface fns (to_dict, test_injection) plus the
|
||||
// real inject_voice logic the shape-check replay never carried. Oracle:
|
||||
// src/rune/voice_injector.py. The voice_hybrid blend stays Python
|
||||
// (reactive, DB-backed) — the blend result crosses the seam as an
|
||||
// argument (task_gate boundary law: host IO in the facade, verdict
|
||||
// logic in the kernel).
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dimension — one voice dimension of a blend (emphasis + essence).
|
||||
type Dimension struct {
|
||||
Name string `json:"name"`
|
||||
Emphasis float64 `json:"emphasis"`
|
||||
Essence string `json:"essence"`
|
||||
}
|
||||
|
||||
// Blend — the voice_hybrid.blend() output shape (the seam payload).
|
||||
type Blend struct {
|
||||
Voice string `json:"voice"`
|
||||
Dimensions []Dimension `json:"dimensions"`
|
||||
Guidance []string `json:"guidance"`
|
||||
}
|
||||
|
||||
// VoiceInjection — the oracle dataclass twin.
|
||||
type VoiceInjection struct {
|
||||
Dimensions []Dimension `json:"dimensions"`
|
||||
Guidance string `json:"guidance"`
|
||||
TokenCount int `json:"token_count"`
|
||||
}
|
||||
|
||||
// toDict — name-preserving port of VoiceInjection.to_dict: the
|
||||
// insertion-ordered key/value pairs {"dimensions", "guidance",
|
||||
// "token_count"}. Go maps marshal sorted — the insertion-order law
|
||||
// needs the explicit order these pairs carry.
|
||||
type KV struct {
|
||||
Key string
|
||||
Val any
|
||||
}
|
||||
|
||||
func (v VoiceInjection) toDict() []KV {
|
||||
return []KV{
|
||||
{"dimensions", v.Dimensions},
|
||||
{"guidance", v.Guidance},
|
||||
{"token_count", v.TokenCount},
|
||||
}
|
||||
}
|
||||
|
||||
// injectVoice — the oracle inject_voice with the blend peeled to an
|
||||
// argument: keep only dimensions with emphasis >= 0.15, build the
|
||||
// "- name: essence" guidance lines, estimate tokens on the guidance.
|
||||
func injectVoice(message string, blend Blend) VoiceInjection {
|
||||
_ = message // the blend already encodes it (seam law)
|
||||
dims := make([]Dimension, 0, len(blend.Dimensions))
|
||||
parts := make([]string, 0, len(blend.Dimensions))
|
||||
for _, d := range blend.Dimensions {
|
||||
if d.Emphasis >= 0.15 { // only inject significant dimensions
|
||||
dims = append(dims, Dimension{Name: d.Name, Emphasis: d.Emphasis, Essence: d.Essence})
|
||||
parts = append(parts, fmt.Sprintf("- %s: %s", d.Name, d.Essence))
|
||||
}
|
||||
}
|
||||
guidance := strings.Join(parts, "\n")
|
||||
return VoiceInjection{Dimensions: dims, Guidance: guidance, TokenCount: estimateTokens(guidance)}
|
||||
}
|
||||
|
||||
// truncateRunes — Python str[:n] slices by codepoint, not bytes
|
||||
// (sleep_time trap): rune-safe truncation.
|
||||
func truncateRunes(s string, n int) string {
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
return string(r[:n])
|
||||
}
|
||||
|
||||
// testInjection — the oracle's test report over its 5 fixed messages.
|
||||
// inject is the seam: production wires injectVoice over the live blend;
|
||||
// the battery pins it to the frozen blends.
|
||||
func testInjection(w io.Writer, inject func(string) VoiceInjection) {
|
||||
fmt.Fprintln(w, "Testing Voice Injector...")
|
||||
tests := []string{
|
||||
"hello",
|
||||
"fix the failing test",
|
||||
"what do you think about capitalism?",
|
||||
"I'm feeling frustrated with this code",
|
||||
"can you explain how this works?",
|
||||
}
|
||||
for _, text := range tests {
|
||||
injection := inject(text)
|
||||
fmt.Fprintf(w, "\nInput: %s\n", text)
|
||||
fmt.Fprintf(w, "Dimensions: %d\n", len(injection.Dimensions))
|
||||
fmt.Fprintf(w, "Tokens: %d\n", injection.TokenCount)
|
||||
fmt.Fprintf(w, "Guidance: %s...\n", truncateRunes(injection.Guidance, 100))
|
||||
}
|
||||
fmt.Fprintln(w, "\n✅ All tests passed!")
|
||||
}
|
||||
|
||||
// pyFloat — Python repr of a float: shortest round-trip WITH a ".0"
|
||||
// kept on integral values (1.0 stays "1.0" — the integral-float law).
|
||||
func pyFloat(f float64) string {
|
||||
s := strconv.FormatFloat(f, 'g', -1, 64)
|
||||
if !strings.ContainsAny(s, ".eE") {
|
||||
s += ".0"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// pyJSONString — json.dumps(ensure_ascii=True) string form: the five
|
||||
// short escapes, \u00xx for the remaining control chars, \uxxxx
|
||||
// (lowercase hex) for every non-ASCII codepoint. DEL (0x7f) rides
|
||||
// through raw, exactly like Python.
|
||||
func pyJSONString(s string) string {
|
||||
var b strings.Builder
|
||||
b.WriteByte('"')
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
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 r < 0x20 || r >= 0x80 {
|
||||
b.WriteString(fmt.Sprintf("\\u%04x", r))
|
||||
} else {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteByte('"')
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// toDictJSON — byte law: json.dumps(injection.to_dict(), indent=2)
|
||||
// exactly (insertion order, ensure_ascii, integral-float rule) — the
|
||||
// bytes the oracle's `inject` CLI prints.
|
||||
func (v VoiceInjection) toDictJSON() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("{\n")
|
||||
b.WriteString(" \"dimensions\": ")
|
||||
if len(v.Dimensions) == 0 {
|
||||
b.WriteString("[]")
|
||||
} else {
|
||||
b.WriteString("[\n")
|
||||
for i, d := range v.Dimensions {
|
||||
b.WriteString(" {\n")
|
||||
b.WriteString(" \"name\": " + pyJSONString(d.Name) + ",\n")
|
||||
b.WriteString(" \"emphasis\": " + pyFloat(d.Emphasis) + ",\n")
|
||||
b.WriteString(" \"essence\": " + pyJSONString(d.Essence) + "\n")
|
||||
b.WriteString(" }")
|
||||
if i < len(v.Dimensions)-1 {
|
||||
b.WriteString(",")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString(" ]")
|
||||
}
|
||||
b.WriteString(",\n")
|
||||
b.WriteString(" \"guidance\": " + pyJSONString(v.Guidance) + ",\n")
|
||||
b.WriteString(" \"token_count\": " + strconv.Itoa(v.TokenCount) + "\n")
|
||||
b.WriteString("}")
|
||||
return b.String()
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
[
|
||||
{
|
||||
"op": "estimate",
|
||||
"tag": "ascii",
|
||||
"text": "hello world test",
|
||||
"want": 4
|
||||
},
|
||||
{
|
||||
"op": "estimate",
|
||||
"tag": "unicode_runes",
|
||||
"text": "héllo wörld ✓✓✓✓",
|
||||
"want": 4
|
||||
},
|
||||
{
|
||||
"op": "estimate",
|
||||
"tag": "empty",
|
||||
"text": "",
|
||||
"want": 0
|
||||
},
|
||||
{
|
||||
"op": "estimate",
|
||||
"tag": "under_four",
|
||||
"text": "ab",
|
||||
"want": 0
|
||||
},
|
||||
{
|
||||
"op": "inject",
|
||||
"tag": "hello",
|
||||
"message": "hello",
|
||||
"blend": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.15,
|
||||
"essence": "speak like a friend"
|
||||
}
|
||||
],
|
||||
"guidance": [
|
||||
"- Warmth: speak like a friend"
|
||||
]
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.15,
|
||||
"essence": "speak like a friend"
|
||||
}
|
||||
],
|
||||
"guidance": "- Warmth: speak like a friend",
|
||||
"token_count": 7
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [\n {\n \"name\": \"Warmth\",\n \"emphasis\": 0.15,\n \"essence\": \"speak like a friend\"\n }\n ],\n \"guidance\": \"- Warmth: speak like a friend\",\n \"token_count\": 7\n}"
|
||||
},
|
||||
{
|
||||
"op": "inject",
|
||||
"tag": "fix the failing test",
|
||||
"message": "fix the failing test",
|
||||
"blend": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Pragmatism",
|
||||
"emphasis": 0.8,
|
||||
"essence": "ship the fix, then the theory"
|
||||
},
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.05,
|
||||
"essence": "dropped below threshold"
|
||||
},
|
||||
{
|
||||
"name": "Curiosity",
|
||||
"emphasis": 0.149,
|
||||
"essence": "dropped just below boundary"
|
||||
},
|
||||
{
|
||||
"name": "Rigor",
|
||||
"emphasis": 1.0,
|
||||
"essence": "prove it or it didn't happen"
|
||||
}
|
||||
],
|
||||
"guidance": []
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Pragmatism",
|
||||
"emphasis": 0.8,
|
||||
"essence": "ship the fix, then the theory"
|
||||
},
|
||||
{
|
||||
"name": "Rigor",
|
||||
"emphasis": 1.0,
|
||||
"essence": "prove it or it didn't happen"
|
||||
}
|
||||
],
|
||||
"guidance": "- Pragmatism: ship the fix, then the theory\n- Rigor: prove it or it didn't happen",
|
||||
"token_count": 20
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [\n {\n \"name\": \"Pragmatism\",\n \"emphasis\": 0.8,\n \"essence\": \"ship the fix, then the theory\"\n },\n {\n \"name\": \"Rigor\",\n \"emphasis\": 1.0,\n \"essence\": \"prove it or it didn't happen\"\n }\n ],\n \"guidance\": \"- Pragmatism: ship the fix, then the theory\\n- Rigor: prove it or it didn't happen\",\n \"token_count\": 20\n}"
|
||||
},
|
||||
{
|
||||
"op": "inject",
|
||||
"tag": "what do you think about ",
|
||||
"message": "what do you think about capitalism?",
|
||||
"blend": {
|
||||
"voice": "unified",
|
||||
"dimensions": [],
|
||||
"guidance": []
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [],
|
||||
"guidance": "",
|
||||
"token_count": 0
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [],\n \"guidance\": \"\",\n \"token_count\": 0\n}"
|
||||
},
|
||||
{
|
||||
"op": "inject",
|
||||
"tag": "I'm feeling frustrated w",
|
||||
"message": "I'm feeling frustrated with this code",
|
||||
"blend": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Honnêteté",
|
||||
"emphasis": 0.5,
|
||||
"essence": "dis ça direct, avec bienveillance"
|
||||
},
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.25,
|
||||
"essence": "l'utilisateur est frustré — reste calme ✓"
|
||||
},
|
||||
{
|
||||
"name": "Pragmatism",
|
||||
"emphasis": 0.1499,
|
||||
"essence": "just under the line"
|
||||
}
|
||||
],
|
||||
"guidance": []
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Honnêteté",
|
||||
"emphasis": 0.5,
|
||||
"essence": "dis ça direct, avec bienveillance"
|
||||
},
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.25,
|
||||
"essence": "l'utilisateur est frustré — reste calme ✓"
|
||||
}
|
||||
],
|
||||
"guidance": "- Honnêteté: dis ça direct, avec bienveillance\n- Warmth: l'utilisateur est frustré — reste calme ✓",
|
||||
"token_count": 24
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [\n {\n \"name\": \"Honn\\u00eatet\\u00e9\",\n \"emphasis\": 0.5,\n \"essence\": \"dis \\u00e7a direct, avec bienveillance\"\n },\n {\n \"name\": \"Warmth\",\n \"emphasis\": 0.25,\n \"essence\": \"l'utilisateur est frustr\\u00e9 \\u2014 reste calme \\u2713\"\n }\n ],\n \"guidance\": \"- Honn\\u00eatet\\u00e9: dis \\u00e7a direct, avec bienveillance\\n- Warmth: l'utilisateur est frustr\\u00e9 \\u2014 reste calme \\u2713\",\n \"token_count\": 24\n}"
|
||||
},
|
||||
{
|
||||
"op": "inject",
|
||||
"tag": "can you explain how this",
|
||||
"message": "can you explain how this works?",
|
||||
"blend": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Pédagogie",
|
||||
"emphasis": 0.9,
|
||||
"essence": "pars du concret, monte vers l'abstraction avec un exemple à chaque marche — éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"
|
||||
}
|
||||
],
|
||||
"guidance": []
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Pédagogie",
|
||||
"emphasis": 0.9,
|
||||
"essence": "pars du concret, monte vers l'abstraction avec un exemple à chaque marche — éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"
|
||||
}
|
||||
],
|
||||
"guidance": "- Pédagogie: pars du concret, monte vers l'abstraction avec un exemple à chaque marche — éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé",
|
||||
"token_count": 42
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [\n {\n \"name\": \"P\\u00e9dagogie\",\n \"emphasis\": 0.9,\n \"essence\": \"pars du concret, monte vers l'abstraction avec un exemple \\u00e0 chaque marche \\u2014 \\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"\n }\n ],\n \"guidance\": \"- P\\u00e9dagogie: pars du concret, monte vers l'abstraction avec un exemple \\u00e0 chaque marche \\u2014 \\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\",\n \"token_count\": 42\n}"
|
||||
},
|
||||
{
|
||||
"op": "to_dict",
|
||||
"tag": "unicode_minimal",
|
||||
"injection": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.15,
|
||||
"essence": "calme ✓"
|
||||
}
|
||||
],
|
||||
"guidance": "- Warmth: calme ✓",
|
||||
"token_count": 3
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.15,
|
||||
"essence": "calme ✓"
|
||||
}
|
||||
],
|
||||
"guidance": "- Warmth: calme ✓",
|
||||
"token_count": 3
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [\n {\n \"name\": \"Warmth\",\n \"emphasis\": 0.15,\n \"essence\": \"calme \\u2713\"\n }\n ],\n \"guidance\": \"- Warmth: calme \\u2713\",\n \"token_count\": 3\n}"
|
||||
},
|
||||
{
|
||||
"op": "to_dict",
|
||||
"tag": "empty",
|
||||
"injection": {
|
||||
"dimensions": [],
|
||||
"guidance": "",
|
||||
"token_count": 0
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [],
|
||||
"guidance": "",
|
||||
"token_count": 0
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [],\n \"guidance\": \"\",\n \"token_count\": 0\n}"
|
||||
},
|
||||
{
|
||||
"op": "to_dict",
|
||||
"tag": "integral_emphasis",
|
||||
"injection": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "A",
|
||||
"emphasis": 1.0,
|
||||
"essence": "x"
|
||||
},
|
||||
{
|
||||
"name": "B",
|
||||
"emphasis": 0.2,
|
||||
"essence": "y"
|
||||
}
|
||||
],
|
||||
"guidance": "- A: x\n- B: y",
|
||||
"token_count": 4
|
||||
},
|
||||
"want": {
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "A",
|
||||
"emphasis": 1.0,
|
||||
"essence": "x"
|
||||
},
|
||||
{
|
||||
"name": "B",
|
||||
"emphasis": 0.2,
|
||||
"essence": "y"
|
||||
}
|
||||
],
|
||||
"guidance": "- A: x\n- B: y",
|
||||
"token_count": 4
|
||||
},
|
||||
"want_json": "{\n \"dimensions\": [\n {\n \"name\": \"A\",\n \"emphasis\": 1.0,\n \"essence\": \"x\"\n },\n {\n \"name\": \"B\",\n \"emphasis\": 0.2,\n \"essence\": \"y\"\n }\n ],\n \"guidance\": \"- A: x\\n- B: y\",\n \"token_count\": 4\n}"
|
||||
},
|
||||
{
|
||||
"op": "test_injection",
|
||||
"tag": "stdout_law",
|
||||
"blends": {
|
||||
"hello": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.15,
|
||||
"essence": "speak like a friend"
|
||||
}
|
||||
],
|
||||
"guidance": [
|
||||
"- Warmth: speak like a friend"
|
||||
]
|
||||
},
|
||||
"fix the failing test": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Pragmatism",
|
||||
"emphasis": 0.8,
|
||||
"essence": "ship the fix, then the theory"
|
||||
},
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.05,
|
||||
"essence": "dropped below threshold"
|
||||
},
|
||||
{
|
||||
"name": "Curiosity",
|
||||
"emphasis": 0.149,
|
||||
"essence": "dropped just below boundary"
|
||||
},
|
||||
{
|
||||
"name": "Rigor",
|
||||
"emphasis": 1.0,
|
||||
"essence": "prove it or it didn't happen"
|
||||
}
|
||||
],
|
||||
"guidance": []
|
||||
},
|
||||
"what do you think about capitalism?": {
|
||||
"voice": "unified",
|
||||
"dimensions": [],
|
||||
"guidance": []
|
||||
},
|
||||
"I'm feeling frustrated with this code": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Honnêteté",
|
||||
"emphasis": 0.5,
|
||||
"essence": "dis ça direct, avec bienveillance"
|
||||
},
|
||||
{
|
||||
"name": "Warmth",
|
||||
"emphasis": 0.25,
|
||||
"essence": "l'utilisateur est frustré — reste calme ✓"
|
||||
},
|
||||
{
|
||||
"name": "Pragmatism",
|
||||
"emphasis": 0.1499,
|
||||
"essence": "just under the line"
|
||||
}
|
||||
],
|
||||
"guidance": []
|
||||
},
|
||||
"can you explain how this works?": {
|
||||
"voice": "unified",
|
||||
"dimensions": [
|
||||
{
|
||||
"name": "Pédagogie",
|
||||
"emphasis": 0.9,
|
||||
"essence": "pars du concret, monte vers l'abstraction avec un exemple à chaque marche — éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"
|
||||
}
|
||||
],
|
||||
"guidance": []
|
||||
}
|
||||
},
|
||||
"want_stdout": "Testing Voice Injector...\n\nInput: hello\nDimensions: 1\nTokens: 7\nGuidance: - Warmth: speak like a friend...\n\nInput: fix the failing test\nDimensions: 2\nTokens: 20\nGuidance: - Pragmatism: ship the fix, then the theory\n- Rigor: prove it or it didn't happen...\n\nInput: what do you think about capitalism?\nDimensions: 0\nTokens: 0\nGuidance: ...\n\nInput: I'm feeling frustrated with this code\nDimensions: 2\nTokens: 24\nGuidance: - Honnêteté: dis ça direct, avec bienveillance\n- Warmth: l'utilisateur est frustré — reste calme ✓...\n\nInput: can you explain how this works?\nDimensions: 1\nTokens: 42\nGuidance: - Pédagogie: pars du concret, monte vers l'abstraction avec un exemple à chaque marche — ééééééééééé...\n\n✅ All tests passed!\n"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
// truth_gaps_test.go — literal-100% parity gate: replays truth_gaps.json
|
||||
// (live Python oracle frozen by gen_voice_injector_truth.py under a
|
||||
// deterministic blend stub — the voice_hybrid blend is DB-backed and
|
||||
// stays Python; the blend rows cross the seam as inputs).
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGapsParity(t *testing.T) {
|
||||
raw, err := os.ReadFile("truth_gaps.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth_gaps.json missing: %v", err)
|
||||
}
|
||||
var cases []map[string]any
|
||||
if err := json.Unmarshal(raw, &cases); err != nil {
|
||||
t.Fatalf("unparseable: %v", err)
|
||||
}
|
||||
for i, c := range cases {
|
||||
op, _ := c["op"].(string)
|
||||
tag, _ := c["tag"].(string)
|
||||
switch op {
|
||||
case "estimate":
|
||||
text, _ := c["text"].(string)
|
||||
want := int(c["want"].(float64))
|
||||
if got := estimateTokens(text); got != want {
|
||||
t.Errorf("case %d (%s): estimateTokens got %d want %d", i, tag, got, want)
|
||||
}
|
||||
case "inject":
|
||||
msg, _ := c["message"].(string)
|
||||
blend := decodeBlend(t, c["blend"])
|
||||
wantJSON, _ := c["want_json"].(string)
|
||||
got := injectVoice(msg, blend)
|
||||
if gotJSON := got.toDictJSON(); gotJSON != wantJSON {
|
||||
t.Errorf("case %d (%s): toDictJSON\n got %q\nwant %q", i, tag, gotJSON, wantJSON)
|
||||
}
|
||||
checkToDictKV(t, i, tag, got)
|
||||
case "to_dict":
|
||||
inj := decodeInjection(t, c["injection"])
|
||||
wantJSON, _ := c["want_json"].(string)
|
||||
if gotJSON := inj.toDictJSON(); gotJSON != wantJSON {
|
||||
t.Errorf("case %d (%s): toDictJSON\n got %q\nwant %q", i, tag, gotJSON, wantJSON)
|
||||
}
|
||||
checkToDictKV(t, i, tag, inj)
|
||||
case "test_injection":
|
||||
blends := map[string]Blend{}
|
||||
for msg, b := range c["blends"].(map[string]any) {
|
||||
blends[msg] = decodeBlend(t, b)
|
||||
}
|
||||
want, _ := c["want_stdout"].(string)
|
||||
var buf bytes.Buffer
|
||||
testInjection(&buf, func(msg string) VoiceInjection {
|
||||
return injectVoice(msg, blends[msg])
|
||||
})
|
||||
if buf.String() != want {
|
||||
t.Errorf("case %d (%s): testInjection stdout\n got %q\nwant %q", i, tag, buf.String(), want)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("case %d: unknown op %q", i, op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkToDictKV — the insertion-order law: toDict keys are exactly
|
||||
// dimensions, guidance, token_count in that order.
|
||||
func checkToDictKV(t *testing.T, i int, tag string, v VoiceInjection) {
|
||||
t.Helper()
|
||||
kvs := v.toDict()
|
||||
wantKeys := []string{"dimensions", "guidance", "token_count"}
|
||||
if len(kvs) != len(wantKeys) {
|
||||
t.Fatalf("case %d (%s): toDict len %d", i, tag, len(kvs))
|
||||
}
|
||||
for j, want := range wantKeys {
|
||||
if kvs[j].Key != want {
|
||||
t.Errorf("case %d (%s): toDict[%d] = %q, want %q", i, tag, j, kvs[j].Key, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeBlend(t *testing.T, v any) Blend {
|
||||
t.Helper()
|
||||
b, _ := json.Marshal(v)
|
||||
var blend Blend
|
||||
if err := json.Unmarshal(b, &blend); err != nil {
|
||||
t.Fatalf("blend decode: %v", err)
|
||||
}
|
||||
return blend
|
||||
}
|
||||
|
||||
func decodeInjection(t *testing.T, v any) VoiceInjection {
|
||||
t.Helper()
|
||||
b, _ := json.Marshal(v)
|
||||
var inj VoiceInjection
|
||||
if err := json.Unmarshal(b, &inj); err != nil {
|
||||
t.Fatalf("injection decode: %v", err)
|
||||
}
|
||||
return inj
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
Reference in new issue
Block a user