Skip to content

Commit 8166c20

Browse files
Jonathan Harrisonclaude
andcommitted
RC+xi v2.3: full adapter roster, full-synthesis mode, self-overclaiming guard, voice-reinforced retrain
Adapters: - Orchestrator + constraint_tracker now load as behavioral adapters (10 total, was 8) - constraint_tracker converted to GGUF + retrained behaviorally (HF Jobs, uv) Features: - Full Adapter Synthesis (SYNTHESIZE ALL): __all__ sentinel + full_synthesis flag through JSON/multipart/SSE + UI button; dropdown gains constraint_tracker/orchestrator Hallucination guard: - Signal 7 (self-overclaiming): catches grandiose self-claims, absolute/perfection language, and fabricated self-metrics (eps=0.998) that previously scored 0% risk - Reliability scan now covers every displayed perspective, not just the synthesis - Fixed latent crash in _check_contradictions (\1 backref in standalone pattern) Prompts: - Consciousness adapter: anti-grandiosity guardrail - Synthesis: perspectives framed as Codette's own first-person lenses (no attribution) Bug fixes: - Constraint parser: ordinary negations ("no word constraint", "no constraints needed") no longer captured as enforced constraints (fixed a repetition loop) - list_sessions() degrades gracefully on a transient drive disconnect - UI: Recent Sessions list no longer clipped (min-height:0 + bottom padding) Tooling: - full_benchmark.py --backend server: score the live llama.cpp + LoRA-hot-swap system - training/train_{constraint,perspectives}_behavioral.py: behavioral retrain scripts Docs: docs/CHANGELOG_2026-05-22.md, README v2.3 note + recent-improvements rows Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 48906bf commit 8166c20

18 files changed

Lines changed: 1217 additions & 74 deletions

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,37 @@ desktop.ini
115115

116116
# Research binary artifacts
117117
research/experiments/Codette_Quantum_Harmonic_Framework.png
118+
119+
# ── Session throwaway: one-off HF job / conversion scripts (2026-05-22) ──
120+
# Ad-hoc submitters and converters — superseded by training/train_*_behavioral.py
121+
auto_submit_hf_job.py
122+
create_hf_conversion_job.py
123+
hf_convert_constraint_job.py
124+
convert_constraint_to_gguf_hf.py
125+
convert_constraint_safetensors_to_gguf.py
126+
submit.py
127+
submit-hf-job.ps1
128+
submit_job_hf_lib.py
129+
submit_job_uv.py
130+
submit_constraint_conversion_job.py
131+
submit_constraint_gguf_job.py
132+
scripts/submit_hf_job.sh
133+
constraint_convert.sh
134+
constraint_gguf_convert.sh
135+
constraint_gguf_convert_uv.sh
136+
run_convert_constraint.bat
137+
adapters/convert_constraint_tracker.py
138+
check_adapters.py
139+
check_safetensors.py
140+
test_constraint_gguf.py
141+
STATUS_ORCHESTRATOR_INTEGRATION.md
142+
SUBMIT_JOB_INSTRUCTIONS.md
143+
benchmark_output.txt
144+
145+
# Downloaded base model + cloned llama.cpp (large, regenerable)
146+
base_model_constraint/
147+
base_model_constraint_convert/
148+
llama.cpp/
149+
150+
# Runtime state artifacts
151+
hf_job_info.json

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Codette is a modular reasoning system that routes queries through specialized co
5555

5656
**v2.2 RC+ξ additions:** Response cutoff fix (`_format_fact()` bolds only the first sentence — inner `**` markers no longer break Markdown rendering), LOCK scrubber tightened to a single precise pattern (prevents over-stripping legitimate content), DISCOVERY tier classifier completed (7 new `AMBIGUOUS_PATTERNS` → 7/7 Discovery attractor accuracy), and benchmark harness hardened with unlimited timeout and mandatory 5 s inter-query delay. Clean benchmark result: **25/25 queries, 0 errors, 100% SIMPLE directness, 7/7 DISCOVERY accuracy, spectral trust 0.754.**
5757

58+
**v2.3 RC+ξ additions:** Full adapter roster online (orchestrator + constraint_tracker now load as behavioral adapters — **10 total**), one-click **Full Adapter Synthesis** (◈ SYNTHESIZE ALL runs every perspective and synthesizes), a new **self-overclaiming hallucination signal** (catches grandiose self-claims and fabricated self-metrics the guard previously scored at 0% risk) with the reliability scan extended across every displayed perspective, a constraint-parser fix (ordinary negations like "no word constraint" no longer become enforced constraints), and a **voice-reinforced behavioral retrain** of all eight perspectives (each on its own reasoning dataset + distinct persona + the four locks) to harden against perspective convergence. See [docs/CHANGELOG_2026-05-22.md](docs/CHANGELOG_2026-05-22.md).
59+
5860
Created by **Jonathan Harrison** (Raiff1982)
5961

6062
## TL;DR
@@ -114,7 +116,7 @@ Codette is a modular reasoning system with published demos, tests, benchmarks, p
114116
- **Automated tests:** [tests](tests)
115117
- **Benchmark suites:** [benchmarks](benchmarks)
116118
- **Saved benchmark reports:** [data/results](data/results)
117-
- **Change transparency:** [docs/CHANGELOG_2026-05-19.md](docs/CHANGELOG_2026-05-19.md) · [docs/CHANGELOG_2026-05-06.md](docs/CHANGELOG_2026-05-06.md) · [docs/CHANGELOG_2026-05-01.md](docs/CHANGELOG_2026-05-01.md) · [docs/CHANGELOG_2026-04-26.md](docs/CHANGELOG_2026-04-26.md) · [docs/CHANGELOG_2026-04-02.md](docs/CHANGELOG_2026-04-02.md)
119+
- **Change transparency:** [docs/CHANGELOG_2026-05-22.md](docs/CHANGELOG_2026-05-22.md) · [docs/CHANGELOG_2026-05-19.md](docs/CHANGELOG_2026-05-19.md) · [docs/CHANGELOG_2026-05-06.md](docs/CHANGELOG_2026-05-06.md) · [docs/CHANGELOG_2026-05-01.md](docs/CHANGELOG_2026-05-01.md) · [docs/CHANGELOG_2026-04-26.md](docs/CHANGELOG_2026-04-26.md) · [docs/CHANGELOG_2026-04-02.md](docs/CHANGELOG_2026-04-02.md)
118120
- **Contributing guide:** [CONTRIBUTING.md](CONTRIBUTING.md)
119121

120122
### Reproduce key claims
@@ -252,7 +254,7 @@ Detailed setup guidance: [docs/deployment/MODEL_SETUP.md](docs/deployment/MODEL_
252254
codette-clean/
253255
|-- inference/ # Server & UI
254256
| |-- codette_server.py # Stdlib HTTP server with SSE streaming
255-
| |-- codette_orchestrator.py # LoRA hot-swap engine (9 adapters, <1ms switch)
257+
| |-- codette_orchestrator.py # LoRA hot-swap engine (10 adapters, <1ms switch)
256258
| |-- codette_forge_bridge.py # Phase 6/7 routing + constraint enforcement
257259
| |-- self_correction.py # Autonomous violation detection & rewrite
258260
| |-- substrate_awareness.py # Hardware-aware cognition (pressure monitoring)
@@ -525,6 +527,15 @@ Note: cocoon memory counts change over time; prefer introspection or health endp
525527
| Dependencies | `requirements.txt` tightened with upper bounds and unused deps removed |
526528
| Legacy fallback alarm | Legacy cocoon fallback now raises warnings and fails smoke tests if triggered |
527529
| Paper v7 | Updated paper, rebuttal, tables, and Kaggle notebook added |
530+
| Full adapter roster | Orchestrator + constraint_tracker now load as behavioral adapters (10 total) |
531+
| Full Adapter Synthesis | ◈ SYNTHESIZE ALL runs every perspective and synthesizes into one answer |
532+
| Self-overclaiming guard | Signal 7 flags grandiose self-claims + fabricated self-metrics; reliability scan now covers every displayed perspective |
533+
| Contradiction-check crash | `_check_contradictions` `\1` backreference fixed (was silently disabled on "always X" responses) |
534+
| Constraint negation parser | Ordinary negations ("no word constraint", "no constraints needed") no longer captured as enforced constraints (fixed a repetition loop) |
535+
| Synthesis voice | Perspectives framed as Codette's own first-person lenses, not external parties she quotes |
536+
| Session list resilience | `list_sessions()` degrades gracefully if the project drive briefly disconnects |
537+
| Benchmark backend | `full_benchmark.py --backend server` scores the live llama.cpp + LoRA-hot-swap system directly |
538+
| Voice-reinforced retrain | All 8 perspectives retrained on their own datasets + distinct personas + the 4 locks (HF Jobs, uv) |
528539

529540
---
530541

benchmarks/full_benchmark.py

Lines changed: 142 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Author: Jonathan Harrison (Raiff's Bits LLC)
1111
"""
1212

13-
import os, sys, json, time, re
13+
import os, sys, json, time, re, argparse
1414
from pathlib import Path
1515
from datetime import datetime
1616

@@ -20,6 +20,15 @@
2020
OLLAMA_URL = os.environ.get("CODETTE_OLLAMA_URL", "http://localhost:11434")
2121
OLLAMA_MODEL = os.environ.get("CODETTE_OLLAMA_MODEL", "codette-adapter-config:latest")
2222

23+
# Live llama.cpp server (codette_server.py) backend
24+
SERVER_URL = os.environ.get("CODETTE_SERVER_URL", "http://localhost:7860")
25+
26+
# Runtime config (set from argparse in __main__)
27+
BACKEND = "ollama" # "ollama" | "server"
28+
MAX_ADAPTERS = 3 # perspectives for the server backend's auto-routing
29+
VERBOSE = False # print full responses as they stream in
30+
PER_QUERY_TIMEOUT = 1200 # seconds — multi-perspective on llama.cpp is slow
31+
2332
# ================================================================
2433
# Test Categories
2534
# ================================================================
@@ -320,31 +329,104 @@ def query_ollama(query, model=OLLAMA_MODEL, num_predict=300):
320329
}
321330

322331

332+
def query_server(query, num_predict=300):
333+
"""Send a query to the live llama.cpp server (/api/chat) and return response + metrics.
334+
335+
Returns the same dict shape as query_ollama so the scorers don't care which backend ran.
336+
"""
337+
payload = json.dumps({
338+
"query": query,
339+
"adapter": None, # let the router pick the perspective(s)
340+
"max_adapters": MAX_ADAPTERS,
341+
"allow_web_search": False,
342+
}).encode()
343+
344+
req = urllib.request.Request(
345+
f"{SERVER_URL}/api/chat",
346+
data=payload,
347+
headers={"Content-Type": "application/json"},
348+
)
349+
350+
start = time.time()
351+
try:
352+
resp = urllib.request.urlopen(req, timeout=PER_QUERY_TIMEOUT)
353+
data = json.loads(resp.read())
354+
elapsed = time.time() - start
355+
356+
if data.get("error"):
357+
return {"response": "", "tokens": 0, "tok_per_sec": 0, "eval_time": 0,
358+
"load_time": 0, "total_time": round(elapsed, 2), "error": data["error"],
359+
"adapter": data.get("adapter"), "perspectives": data.get("perspectives")}
360+
361+
content = (data.get("response") or "").strip()
362+
tokens = data.get("tokens", 0) or 0
363+
eval_dur = data.get("time", elapsed) or elapsed
364+
tps = tokens / eval_dur if eval_dur > 0 else 0
365+
366+
return {
367+
"response": content,
368+
"tokens": tokens,
369+
"tok_per_sec": round(tps, 1),
370+
"eval_time": round(eval_dur, 2),
371+
"load_time": 0,
372+
"total_time": round(elapsed, 2),
373+
"error": None,
374+
"adapter": data.get("adapter"),
375+
"perspectives": data.get("perspectives"),
376+
"reliability": (data.get("confidence_analysis") or {}).get("response_confidence"),
377+
"hallucination_signals": (data.get("confidence_analysis") or {}).get("hallucination_signals"),
378+
}
379+
except Exception as e:
380+
return {"response": "", "tokens": 0, "tok_per_sec": 0, "eval_time": 0,
381+
"load_time": 0, "total_time": round(time.time() - start, 2), "error": str(e)}
382+
383+
384+
def query_model(query, num_predict=300):
385+
"""Dispatch a query to the selected backend."""
386+
if BACKEND == "server":
387+
return query_server(query, num_predict=num_predict)
388+
return query_ollama(query, num_predict=num_predict)
389+
390+
323391
def run_benchmark():
324392
"""Run the full benchmark suite."""
325393
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
326394
results_dir = Path(__file__).parent / "results"
327395
results_dir.mkdir(exist_ok=True)
328396

397+
backend_label = "llama.cpp server" if BACKEND == "server" else "Ollama"
398+
model_label = SERVER_URL if BACKEND == "server" else OLLAMA_MODEL
399+
329400
print("=" * 70)
330401
print(f" CODETTE FULL BENCHMARK SUITE")
331-
print(f" Model: {OLLAMA_MODEL}")
402+
print(f" Backend: {backend_label} ({model_label})")
332403
print(f" Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
404+
if BACKEND == "server":
405+
print(f" max_adapters={MAX_ADAPTERS} | per-query timeout={PER_QUERY_TIMEOUT}s | verbose={VERBOSE}")
333406
print("=" * 70)
334407

335-
# Verify Ollama is running
336-
try:
337-
resp = urllib.request.urlopen(f"{OLLAMA_URL}/api/ps", timeout=5)
338-
ps_data = json.loads(resp.read())
339-
loaded = [m["name"] for m in ps_data.get("models", [])]
340-
print(f" Ollama: running, {len(loaded)} models loaded")
341-
if loaded:
342-
for m in loaded:
343-
vram = [x.get("size_vram", 0) for x in ps_data["models"] if x["name"] == m]
344-
gpu = "GPU" if vram and vram[0] > 0 else "CPU"
345-
print(f" - {m} ({gpu})")
346-
except:
347-
print(" WARNING: Ollama not responding")
408+
# Verify the selected backend is responding
409+
if BACKEND == "server":
410+
try:
411+
resp = urllib.request.urlopen(f"{SERVER_URL}/api/status", timeout=5)
412+
st = json.loads(resp.read())
413+
print(f" Server: running | state={st.get('state', '?')} "
414+
f"| adapters={st.get('adapters', st.get('adapter_count', '?'))}")
415+
except Exception as e:
416+
print(f" WARNING: server not responding at {SERVER_URL} ({e})")
417+
else:
418+
try:
419+
resp = urllib.request.urlopen(f"{OLLAMA_URL}/api/ps", timeout=5)
420+
ps_data = json.loads(resp.read())
421+
loaded = [m["name"] for m in ps_data.get("models", [])]
422+
print(f" Ollama: running, {len(loaded)} models loaded")
423+
if loaded:
424+
for m in loaded:
425+
vram = [x.get("size_vram", 0) for x in ps_data["models"] if x["name"] == m]
426+
gpu = "GPU" if vram and vram[0] > 0 else "CPU"
427+
print(f" - {m} ({gpu})")
428+
except:
429+
print(" WARNING: Ollama not responding")
348430

349431
print()
350432

@@ -368,7 +450,7 @@ def run_benchmark():
368450
short_q = query[:60] + "..." if len(query) > 60 else query
369451
print(f"\n [{i+1}/{len(tests)}] {short_q}")
370452

371-
result = query_ollama(query)
453+
result = query_model(query)
372454
total_tokens += result["tokens"]
373455
total_time += result["total_time"]
374456

@@ -392,8 +474,17 @@ def run_benchmark():
392474
else:
393475
score, detail = score_generic(result, test)
394476

395-
response_preview = result["response"][:120].replace("\n", " ")
396-
print(f" Response: {response_preview}...")
477+
if VERBOSE:
478+
if result.get("adapter") is not None:
479+
print(f" Adapter/route: {result.get('adapter')}")
480+
print(" Response:")
481+
for line in result["response"].splitlines() or [""]:
482+
print(f" {line}")
483+
if result.get("reliability") is not None:
484+
print(f" Reliability: {result['reliability']} | signals: {result.get('hallucination_signals')}")
485+
else:
486+
response_preview = result["response"][:120].replace("\n", " ")
487+
print(f" Response: {response_preview}...")
397488
print(f" Score: {score:.2f} | {detail}")
398489
print(f" {result['tokens']} tok | {result['tok_per_sec']} tok/s | {result['total_time']}s")
399490

@@ -438,15 +529,14 @@ def run_benchmark():
438529
print(f" Total tokens: {total_tokens}")
439530
print(f" Total time: {total_time:.1f}s")
440531
print(f" Average speed: {avg_tps:.1f} tok/s")
441-
print(f" Model: {OLLAMA_MODEL}")
442-
print(f" Backend: Ollama (Vulkan GPU)")
532+
print(f" Backend: {backend_label} ({model_label})")
443533
print("=" * 70)
444534

445535
# -- Save results --
446536
output = {
447537
"timestamp": timestamp,
448-
"model": OLLAMA_MODEL,
449-
"backend": "ollama_vulkan_gpu",
538+
"model": model_label,
539+
"backend": "llama_cpp_server" if BACKEND == "server" else "ollama_vulkan_gpu",
450540
"overall_score": round(overall, 4),
451541
"total_tests": test_count,
452542
"total_tokens": total_tokens,
@@ -472,8 +562,7 @@ def run_benchmark():
472562
with open(md_path, "w", encoding="utf-8") as f:
473563
f.write(f"# Codette Benchmark Report\n\n")
474564
f.write(f"**Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} \n")
475-
f.write(f"**Model:** {OLLAMA_MODEL} \n")
476-
f.write(f"**Backend:** Ollama + Vulkan (Intel Arc 140V GPU) \n")
565+
f.write(f"**Backend:** {backend_label} ({model_label}) \n")
477566
f.write(f"**Overall Score:** {overall:.1%} \n\n")
478567

479568
f.write(f"## Summary\n\n")
@@ -505,14 +594,34 @@ def run_benchmark():
505594

506595

507596
if __name__ == "__main__":
508-
# Auto-detect model
509-
try:
510-
resp = urllib.request.urlopen(f"{OLLAMA_URL}/api/ps", timeout=5)
511-
ps = json.loads(resp.read())
512-
if ps.get("models"):
513-
OLLAMA_MODEL = ps["models"][0]["name"]
514-
print(f" Auto-detected model: {OLLAMA_MODEL}")
515-
except:
516-
pass
597+
parser = argparse.ArgumentParser(description="Codette full benchmark suite")
598+
parser.add_argument("--backend", choices=["ollama", "server"], default="ollama",
599+
help="ollama (:11434) or the live llama.cpp server (:7860)")
600+
parser.add_argument("--server-url", default=SERVER_URL,
601+
help="base URL of the llama.cpp server (server backend)")
602+
parser.add_argument("--max-adapters", type=int, default=MAX_ADAPTERS,
603+
help="perspectives the server router may use per query")
604+
parser.add_argument("--timeout", type=int, default=PER_QUERY_TIMEOUT,
605+
help="per-query timeout in seconds (server multi-perspective is slow)")
606+
parser.add_argument("--verbose", action="store_true",
607+
help="print full responses + route + reliability as they run")
608+
args = parser.parse_args()
609+
610+
BACKEND = args.backend
611+
SERVER_URL = args.server_url
612+
MAX_ADAPTERS = args.max_adapters
613+
PER_QUERY_TIMEOUT = args.timeout
614+
VERBOSE = args.verbose
615+
616+
if BACKEND == "ollama":
617+
# Auto-detect the loaded Ollama model
618+
try:
619+
resp = urllib.request.urlopen(f"{OLLAMA_URL}/api/ps", timeout=5)
620+
ps = json.loads(resp.read())
621+
if ps.get("models"):
622+
OLLAMA_MODEL = ps["models"][0]["name"]
623+
print(f" Auto-detected model: {OLLAMA_MODEL}")
624+
except:
625+
pass
517626

518627
run_benchmark()

0 commit comments

Comments
 (0)