The conversation demo's Scene 4 was emitting CORE's raw production
teaching-grounded surface, which reads engineer-y for a layperson:
narrative — teaching-grounded (cognition_chains_v1):
rhetoric.narrative; language.discourse. narrative reveals
meaning (cognition.meaning). No session evidence yet.
The production format is the trust-boundary contract (12+ tests + eval
byte-equivalence + several ADRs depend on it), so it stays unchanged.
This change adds a demo-only display layer that rewrites the same
surface to put the propositional sentence first, with provenance as a
trailing parenthetical:
Narrative reveals meaning. (teaching-grounded from
cognition_chains_v1 — narrative: rhetoric.narrative;
language.discourse; final term: cognition.meaning.
No session evidence yet.)
Trust-boundary preserving:
- Only fires when grounding_source == "teaching" AND surface matches
the production format.
- Every load-bearing token preserved (subject, connective, object,
corpus_id, semantic_domains, "No session evidence yet").
- Pack-grounded surfaces + discourse-planner surfaces pass through
unchanged.
- JSON report's `surface` field still carries the raw production
surface — only the chat-style print is humanised.
Test gate: 2 new tests pin the rewrite contract (proposition-first,
all load-bearing tokens preserved, passthrough for non-teaching).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
466 lines
17 KiB
Python
466 lines
17 KiB
Python
"""Conversation demo — layperson-facing chat transcript.
|
|
|
|
Four scenes that show CORE actually being used, framed as a chat
|
|
transcript with plain-English notes between turns. No metric tables,
|
|
no flag jargon — just ``You: …`` / ``CORE: …`` and a short caption
|
|
after each turn that explains what just happened.
|
|
|
|
Scenes:
|
|
|
|
1. Pack lookup — "What is truth?"
|
|
Shows the system answering from its
|
|
lexicon, deterministically.
|
|
|
|
2. Teaching-chain — "Walk me through recall."
|
|
Shows CORE chaining reviewed facts to
|
|
produce a multi-sentence answer.
|
|
|
|
3. Compound prompt — "What is truth, and why does it matter?"
|
|
Shows CORE handling both clauses,
|
|
composing two sub-answers in order.
|
|
|
|
4. Cold turn → learn — "Why does narrative exist?"
|
|
Shows CORE saying "I haven't learned
|
|
this yet", an operator teaching it, then
|
|
the same prompt answered. The full
|
|
learning loop in plain English.
|
|
|
|
Stream mode (default) emits the response word-by-word with a small
|
|
inter-word delay so the layperson sees the answer "arriving live".
|
|
This is presentation only — the underlying surface is byte-identical
|
|
to the non-streamed version, because CORE's articulation path is
|
|
deterministic.
|
|
|
|
``--no-stream`` disables the delay (CI / tests / fast capture).
|
|
``--json`` emits a structured report and suppresses all chat output.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
import textwrap
|
|
import time
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from chat.runtime import ChatRuntime
|
|
from core.config import RuntimeConfig
|
|
|
|
|
|
# Production teaching-grounded surface format (chat/teaching_grounding.py):
|
|
# "{subject} — teaching-grounded ({corpus_id}): {ds1}; {ds2}.
|
|
# {subject} {conn} {object} ({do}). No session evidence yet."
|
|
#
|
|
# Semantic domains contain dots ("rhetoric.narrative"), so we can't
|
|
# split on '.' alone. Instead we anchor on the fixed trailing
|
|
# "No session evidence yet.", the corpus-id parenthetical, and the
|
|
# fact that the propositional sentence begins with the subject lemma
|
|
# (which we capture from the header). This makes the parse
|
|
# unambiguous against the live format.
|
|
_TEACHING_HEADER_RE = re.compile(
|
|
r"^(?P<subject>[A-Za-z][A-Za-z_-]*)\s*—\s*teaching-grounded\s*"
|
|
r"\((?P<corpus_id>[^)]+)\):\s*"
|
|
)
|
|
_TEACHING_TAIL_LITERAL = "No session evidence yet."
|
|
|
|
|
|
def _humanize_surface(surface: str, *, grounding_source: str) -> str:
|
|
"""Layperson-friendly rewrite of CORE's surface for display.
|
|
|
|
Trust-boundary preserving:
|
|
* Only fires for ``grounding_source == "teaching"`` surfaces
|
|
matching the production format.
|
|
* Keeps every load-bearing token (subject, connective, object,
|
|
corpus_id, semantic_domains, "No session evidence yet").
|
|
* Reorders so the propositional sentence reads first, with
|
|
provenance as a trailing parenthetical.
|
|
|
|
Production surface is unchanged — this is presentation only and is
|
|
not applied to the JSON report's ``surface`` field.
|
|
"""
|
|
if grounding_source != "teaching":
|
|
return surface
|
|
text = surface.strip()
|
|
if not text.endswith(_TEACHING_TAIL_LITERAL):
|
|
return surface
|
|
header = _TEACHING_HEADER_RE.match(text)
|
|
if header is None:
|
|
return surface
|
|
subject = header.group("subject")
|
|
corpus_id = header.group("corpus_id").strip()
|
|
body = text[header.end():-len(_TEACHING_TAIL_LITERAL)].rstrip().rstrip(".").strip()
|
|
# Body shape: "{ds1}; {ds2}. {subject} {conn} {object} ({do})"
|
|
# The split between subject_domains and the sentence is the FIRST
|
|
# ". " followed by the subject lemma.
|
|
sentence_marker = f". {subject} "
|
|
idx = body.find(sentence_marker)
|
|
if idx == -1:
|
|
return surface
|
|
subject_domains = body[:idx].strip()
|
|
sentence_and_obj = body[idx + 2:].strip() # skip ". "
|
|
# Trailing "(do)" parenthetical:
|
|
paren_open = sentence_and_obj.rfind("(")
|
|
paren_close = sentence_and_obj.rfind(")")
|
|
if paren_open == -1 or paren_close == -1 or paren_close < paren_open:
|
|
return surface
|
|
sentence = sentence_and_obj[:paren_open].strip()
|
|
object_domains = sentence_and_obj[paren_open + 1:paren_close].strip()
|
|
if not sentence:
|
|
return surface
|
|
sentence_cased = sentence[:1].upper() + sentence[1:]
|
|
return (
|
|
f"{sentence_cased}. "
|
|
f"(teaching-grounded from {corpus_id} — "
|
|
f"{subject}: {subject_domains}; "
|
|
f"final term: {object_domains}. "
|
|
f"No session evidence yet.)"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Streaming presentation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
_WORD_DELAY_SECONDS: float = 0.04 # ~25 words/second; conversational pace
|
|
_CARET_DELAY_SECONDS: float = 0.012 # per-char delay for the "typed" prompt
|
|
|
|
|
|
def _stream_write(text: str, delay: float = _CARET_DELAY_SECONDS) -> None:
|
|
"""Write text to stdout with a per-character delay."""
|
|
for ch in text:
|
|
sys.stdout.write(ch)
|
|
sys.stdout.flush()
|
|
if delay > 0:
|
|
time.sleep(delay)
|
|
|
|
|
|
def _stream_words(text: str, *, prefix: str = " ", width: int = 60,
|
|
delay: float = _WORD_DELAY_SECONDS) -> None:
|
|
"""Emit ``text`` word-by-word, wrapped to ``width`` after ``prefix``.
|
|
|
|
The caller is expected to have already written the first-line
|
|
label (e.g. ``" CORE: "``), so no prefix is written on the very
|
|
first line — only on wrapped continuation lines.
|
|
"""
|
|
line = "" # tracks rendered width on current line; caller wrote the label
|
|
first_line = True
|
|
for word in text.split():
|
|
if first_line:
|
|
sep = "" if not line else " "
|
|
candidate_width = len(line) + len(sep) + len(word)
|
|
else:
|
|
sep = "" if not line else " "
|
|
candidate_width = len(line) + len(sep) + len(word)
|
|
if candidate_width > width and line:
|
|
sys.stdout.write("\n")
|
|
sys.stdout.write(prefix)
|
|
line = ""
|
|
first_line = False
|
|
sep = ""
|
|
sys.stdout.write(sep + word)
|
|
sys.stdout.flush()
|
|
line = line + sep + word
|
|
if delay > 0:
|
|
time.sleep(delay)
|
|
sys.stdout.write("\n")
|
|
sys.stdout.flush()
|
|
|
|
|
|
def _stream_note(text: str, *, prefix: str = " ← ", width: int = 56) -> None:
|
|
"""Emit a plain-English caption after a CORE turn."""
|
|
wrapped = textwrap.fill(
|
|
text,
|
|
width=width,
|
|
initial_indent=prefix,
|
|
subsequent_indent=" ",
|
|
)
|
|
sys.stdout.write("\n")
|
|
for line in wrapped.splitlines():
|
|
sys.stdout.write(line + "\n")
|
|
sys.stdout.flush()
|
|
time.sleep(_WORD_DELAY_SECONDS)
|
|
|
|
|
|
def _scene_header(num: int, title: str) -> None:
|
|
sys.stdout.write("\n")
|
|
sys.stdout.write("─" * 64 + "\n")
|
|
sys.stdout.write(f" Scene {num} — {title}\n")
|
|
sys.stdout.write("─" * 64 + "\n\n")
|
|
sys.stdout.flush()
|
|
|
|
|
|
def _emit_turn(
|
|
prompt: str,
|
|
response_text: str,
|
|
note: str,
|
|
*,
|
|
stream: bool,
|
|
grounding_source: str = "",
|
|
) -> None:
|
|
"""Render one You/CORE turn with a caption.
|
|
|
|
``stream=True`` adds per-character / per-word delays (live feel).
|
|
``stream=False`` prints the same layout instantly (CI / tests /
|
|
fast capture).
|
|
|
|
``response_text`` is humanised for display only — when it matches
|
|
the production teaching-grounded format, it's rewritten to put
|
|
the propositional sentence first and provenance in a trailing
|
|
parenthetical. The raw surface remains in the JSON report.
|
|
"""
|
|
displayed = _humanize_surface(response_text, grounding_source=grounding_source)
|
|
if stream:
|
|
sys.stdout.write(" You: ")
|
|
_stream_write(prompt, _CARET_DELAY_SECONDS)
|
|
sys.stdout.write("\n\n")
|
|
sys.stdout.write(" CORE: ")
|
|
sys.stdout.flush()
|
|
time.sleep(0.25) # tiny "thinking" pause
|
|
_stream_words(displayed, prefix=" ", width=58)
|
|
_stream_note(note)
|
|
else:
|
|
sys.stdout.write(f" You: {prompt}\n\n")
|
|
wrapped_response = textwrap.fill(
|
|
displayed, width=58,
|
|
initial_indent=" ", subsequent_indent=" ",
|
|
)
|
|
sys.stdout.write(f" CORE: {wrapped_response.lstrip()}\n\n")
|
|
wrapped_note = textwrap.fill(
|
|
note, width=56,
|
|
initial_indent=" ← ", subsequent_indent=" ",
|
|
)
|
|
sys.stdout.write(f"{wrapped_note}\n")
|
|
sys.stdout.flush()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Report shapes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class TurnRecord:
|
|
scene: str
|
|
prompt: str
|
|
surface: str
|
|
grounding_source: str
|
|
note: str
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"scene": self.scene,
|
|
"prompt": self.prompt,
|
|
"surface": self.surface,
|
|
"grounding_source": self.grounding_source,
|
|
"note": self.note,
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ConversationReport:
|
|
turns: tuple[TurnRecord, ...]
|
|
learning_loop_closed: bool
|
|
active_corpus_byte_identical: bool
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"turns": [t.as_dict() for t in self.turns],
|
|
"learning_loop_closed": self.learning_loop_closed,
|
|
"active_corpus_byte_identical": self.active_corpus_byte_identical,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CORE wrappers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _ask(prompt: str, *, planner: bool = True) -> tuple[str, str]:
|
|
rt = ChatRuntime(config=RuntimeConfig(discourse_planner=planner))
|
|
response = rt.chat(prompt)
|
|
return response.surface, response.grounding_source
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scenes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _scene1_pack_lookup(*, show: bool, stream: bool) -> TurnRecord:
|
|
prompt = "What is truth?"
|
|
if show:
|
|
_scene_header(1, "Asking CORE to define a concept")
|
|
surface, grounding = _ask(prompt, planner=False)
|
|
note = (
|
|
"CORE looked this up in its curated lexicon. Every word in the "
|
|
"answer traces to a reviewed source — same answer every time, no "
|
|
"internet, no guessing."
|
|
)
|
|
if show:
|
|
_emit_turn(prompt, surface, note, stream=stream, grounding_source=grounding)
|
|
return TurnRecord(
|
|
scene="S1_pack_lookup", prompt=prompt, surface=surface,
|
|
grounding_source=grounding, note=note,
|
|
)
|
|
|
|
|
|
def _scene2_teaching_chain(*, show: bool, stream: bool) -> TurnRecord:
|
|
prompt = "Walk me through recall."
|
|
if show:
|
|
_scene_header(2, "Asking CORE to walk through a concept")
|
|
surface, grounding = _ask(prompt, planner=True)
|
|
note = (
|
|
"The second sentence wasn't memorised — CORE walked a reviewed "
|
|
"teaching chain: recall → reveals → memory. Each hop is a fact "
|
|
"an operator approved."
|
|
)
|
|
if show:
|
|
_emit_turn(prompt, surface, note, stream=stream, grounding_source=grounding)
|
|
return TurnRecord(
|
|
scene="S2_teaching_chain", prompt=prompt, surface=surface,
|
|
grounding_source=grounding, note=note,
|
|
)
|
|
|
|
|
|
def _scene3_compound(*, show: bool, stream: bool) -> TurnRecord:
|
|
prompt = "What is truth, and why does it matter?"
|
|
if show:
|
|
_scene_header(3, "Asking CORE a two-part question")
|
|
surface, grounding = _ask(prompt, planner=True)
|
|
note = (
|
|
"CORE split the question at the comma, answered both halves, and "
|
|
"stitched them together in order — every sentence still grounded "
|
|
"in the lexicon or in a reviewed chain."
|
|
)
|
|
if show:
|
|
_emit_turn(prompt, surface, note, stream=stream, grounding_source=grounding)
|
|
return TurnRecord(
|
|
scene="S3_compound", prompt=prompt, surface=surface,
|
|
grounding_source=grounding, note=note,
|
|
)
|
|
|
|
|
|
def _scene4_learning_loop(*, show: bool, stream: bool) -> tuple[TurnRecord, TurnRecord, bool, bool]:
|
|
"""Cold turn → operator teaches → re-ask.
|
|
|
|
Reuses the production learning-loop demo so the underlying
|
|
propose/replay/accept machinery is exactly what ships.
|
|
"""
|
|
from evals.learning_loop.run_demo import run_demo as run_loop
|
|
|
|
prompt = "Why does narrative exist?"
|
|
if show:
|
|
_scene_header(4, "Teaching CORE something new, then re-asking")
|
|
sys.stdout.write(" (This scene runs CORE's reviewed-learning loop end-to-end:\n")
|
|
sys.stdout.write(" cold turn → operator proposes a chain → safety/replay gate\n")
|
|
sys.stdout.write(" confirms no regression → operator accepts → same prompt is\n")
|
|
sys.stdout.write(" now grounded. The active corpus on disk is not mutated.)\n\n")
|
|
sys.stdout.flush()
|
|
|
|
# Run the real learning-loop demo (suppressed output) to get the
|
|
# before/after surfaces deterministically.
|
|
import contextlib, io
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
ll = run_loop(emit_json=True)
|
|
|
|
before_surface = ll["before"]["surface"]
|
|
before_grounding = ll["before"]["grounding_source"]
|
|
after_surface = ll["after"]["surface"]
|
|
after_grounding = ll["after"]["grounding_source"]
|
|
loop_closed = bool(ll["learning_loop_closed"])
|
|
byte_identical = bool(ll["active_corpus_byte_identical"])
|
|
|
|
before_note = (
|
|
"CORE refuses to make something up. It says it hasn't learned this "
|
|
"yet and points to where a reviewed chain would help — instead of "
|
|
"fabricating an answer."
|
|
)
|
|
after_note = (
|
|
"An operator reviewed and accepted one new chain "
|
|
"(narrative → reveals → meaning). A replay gate first confirmed it "
|
|
"wouldn't regress anything CORE already knows. Now the same prompt "
|
|
"is answered — with full provenance back to that one accept."
|
|
)
|
|
|
|
if show:
|
|
_emit_turn(
|
|
prompt, before_surface, before_note,
|
|
stream=stream, grounding_source=before_grounding,
|
|
)
|
|
sys.stdout.write("\n")
|
|
sys.stdout.write(" ┄ ┄ ┄ operator teaches CORE one new fact ┄ ┄ ┄\n\n")
|
|
sys.stdout.flush()
|
|
if stream:
|
|
time.sleep(0.6)
|
|
_emit_turn(
|
|
prompt, after_surface, after_note,
|
|
stream=stream, grounding_source=after_grounding,
|
|
)
|
|
|
|
before = TurnRecord(
|
|
scene="S4a_cold_turn", prompt=prompt, surface=before_surface,
|
|
grounding_source=before_grounding, note=before_note,
|
|
)
|
|
after = TurnRecord(
|
|
scene="S4b_after_teaching", prompt=prompt, surface=after_surface,
|
|
grounding_source=after_grounding, note=after_note,
|
|
)
|
|
return before, after, loop_closed, byte_identical
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Public entry point
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def run_demo(*, emit_json: bool = False, stream: bool = True) -> dict[str, Any]:
|
|
"""Run all four scenes and return a structured report.
|
|
|
|
``emit_json=True`` suppresses every chat-style print; only the
|
|
final JSON object will be emitted by the caller. ``stream=False``
|
|
keeps the chat layout but skips the per-character / per-word
|
|
delays (used by tests and ``--no-stream``).
|
|
"""
|
|
show = not emit_json
|
|
actual_stream = show and stream
|
|
|
|
if show:
|
|
sys.stdout.write("\n")
|
|
sys.stdout.write("═" * 64 + "\n")
|
|
sys.stdout.write(" Conversation with CORE — live walkthrough\n")
|
|
sys.stdout.write("═" * 64 + "\n")
|
|
sys.stdout.write(
|
|
"\n CORE is a deterministic cognitive engine. It doesn't run\n"
|
|
" an LLM, it doesn't sample, it doesn't search the web. Every\n"
|
|
" word in every answer below traces to a reviewed source.\n"
|
|
" Run this demo twice — you'll get the same surfaces.\n"
|
|
)
|
|
sys.stdout.flush()
|
|
|
|
s1 = _scene1_pack_lookup(show=show, stream=actual_stream)
|
|
s2 = _scene2_teaching_chain(show=show, stream=actual_stream)
|
|
s3 = _scene3_compound(show=show, stream=actual_stream)
|
|
s4_before, s4_after, loop_closed, byte_identical = _scene4_learning_loop(
|
|
show=show, stream=actual_stream,
|
|
)
|
|
|
|
turns = (s1, s2, s3, s4_before, s4_after)
|
|
report = ConversationReport(
|
|
turns=turns,
|
|
learning_loop_closed=loop_closed,
|
|
active_corpus_byte_identical=byte_identical,
|
|
)
|
|
|
|
if show:
|
|
sys.stdout.write("\n")
|
|
sys.stdout.write("═" * 64 + "\n")
|
|
sys.stdout.write(" Done. Everything above is deterministic and replayable.\n")
|
|
sys.stdout.write("═" * 64 + "\n\n")
|
|
sys.stdout.flush()
|
|
|
|
return report.as_dict()
|
|
|
|
|
|
__all__ = ["run_demo"]
|