|
@@ -603,20 +603,23 @@ class FakeTriage:
|
|
|
)
|
|
)
|
|
|
return "BRIEF-PROMPT"
|
|
return "BRIEF-PROMPT"
|
|
|
|
|
|
|
|
- def build_pinned_brief_prompt(self, current_brief, deltas, signals):
|
|
|
|
|
|
|
+ def build_ledger_prompt(self, narrative, ledger, deltas, signals):
|
|
|
self.prompt_calls.append(
|
|
self.prompt_calls.append(
|
|
|
(tuple(deltas), dict(signals) if isinstance(signals, dict) else signals)
|
|
(tuple(deltas), dict(signals) if isinstance(signals, dict) else signals)
|
|
|
)
|
|
)
|
|
|
|
|
+ ledger_text = "\n".join("- " + f for f in ledger) or "(none yet)"
|
|
|
body = "\n".join("- " + d for d in deltas)
|
|
body = "\n".join("- " + d for d in deltas)
|
|
|
return (
|
|
return (
|
|
|
- f"CURRENT BRIEF:\n{current_brief or '(none yet)'}\n\nNEW UTTERANCES:\n{body}"
|
|
|
|
|
|
|
+ f"CURRENT LEDGER:\n{ledger_text}\n\n"
|
|
|
|
|
+ f"CURRENT BRIEF:\n{narrative or '(none yet)'}\n\n"
|
|
|
|
|
+ f"NEW UTTERANCES:\n{body}"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- def extract_bullets(self, brief):
|
|
|
|
|
- return _real_analyzer.extract_bullets(brief)
|
|
|
|
|
|
|
+ def parse_ledger_reply(self, text, truncated=False):
|
|
|
|
|
+ return _real_analyzer.parse_ledger_reply(text, truncated=truncated)
|
|
|
|
|
|
|
|
- def restore_missing_bullets(self, new_brief, prior_brief):
|
|
|
|
|
- return _real_analyzer.restore_missing_bullets(new_brief, prior_brief)
|
|
|
|
|
|
|
+ def parse_facts(self, block):
|
|
|
|
|
+ return _real_analyzer.parse_facts(block)
|
|
|
|
|
|
|
|
def parse_brief(self, text):
|
|
def parse_brief(self, text):
|
|
|
if self._parsed is _AUTO:
|
|
if self._parsed is _AUTO:
|
|
@@ -679,6 +682,12 @@ def _ok_reply(text):
|
|
|
return "ok"
|
|
return "ok"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _brief_reply(narrative="", facts=None):
|
|
|
|
|
+ """Build a ledger-format reply the analyzer's parser can consume."""
|
|
|
|
|
+ body = "\n".join("- " + fact for fact in (facts or [])) or "none"
|
|
|
|
|
+ return f"NEW FACTS:\n{body}\n\nBRIEF:\n{narrative}"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _triage_service(
|
|
def _triage_service(
|
|
|
triage=None,
|
|
triage=None,
|
|
|
lane=None,
|
|
lane=None,
|
|
@@ -711,7 +720,7 @@ def _triage_service(
|
|
|
time_fn=clock,
|
|
time_fn=clock,
|
|
|
triage=triage,
|
|
triage=triage,
|
|
|
laya_lane=lane,
|
|
laya_lane=lane,
|
|
|
- brief_fn=brief_fn,
|
|
|
|
|
|
|
+ brief_fn=_wrap_brief_fn(brief_fn),
|
|
|
brief_reset=brief_reset,
|
|
brief_reset=brief_reset,
|
|
|
brief_file=brief_file,
|
|
brief_file=brief_file,
|
|
|
chat_fn=chat_fn,
|
|
chat_fn=chat_fn,
|
|
@@ -721,6 +730,33 @@ def _triage_service(
|
|
|
return service, emitted, clock, triage, lane
|
|
return service, emitted, clock, triage, lane
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _wrap_brief_fn(brief_fn):
|
|
|
|
|
+ """Adapt a simple `prompt -> text` stub to the engine's call signature.
|
|
|
|
|
+
|
|
|
|
|
+ A plain string return is treated as the volatile narrative half of a
|
|
|
|
|
+ ledger reply, so existing stubs keep reading naturally.
|
|
|
|
|
+ """
|
|
|
|
|
+ if brief_fn is None:
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ def wrapped(prompt, on_io=None, finalize=None):
|
|
|
|
|
+ kwargs = {}
|
|
|
|
|
+ if on_io is not None:
|
|
|
|
|
+ kwargs["on_io"] = on_io
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = brief_fn(prompt, **kwargs)
|
|
|
|
|
+ except TypeError:
|
|
|
|
|
+ result = brief_fn(prompt)
|
|
|
|
|
+ if finalize is not None:
|
|
|
|
|
+ result = finalize(result, "stop")
|
|
|
|
|
+ is_ledger = "BRIEF:" in result or "NEW FACTS:" in result
|
|
|
|
|
+ if isinstance(result, str) and result and not is_ledger:
|
|
|
|
|
+ result = _brief_reply(narrative=result)
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+ return wrapped
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _triage_events(emitted):
|
|
def _triage_events(emitted):
|
|
|
return [e for e in emitted if e["type"] == "triage"]
|
|
return [e for e in emitted if e["type"] == "triage"]
|
|
|
|
|
|
|
@@ -1471,57 +1507,56 @@ def _seed_brief(service, *deltas):
|
|
|
return service._brief_token
|
|
return service._brief_token
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_retention_guard_reappends_fact_dropped_by_model():
|
|
|
|
|
- """A prior bullet the model omitted is deterministically restored."""
|
|
|
|
|
- prior = (
|
|
|
|
|
- "Summary: rolling\n\nPrior Details:\n"
|
|
|
|
|
- "- The budget was $305 million.\n"
|
|
|
|
|
- "- Sense could find prey.\n"
|
|
|
|
|
- )
|
|
|
|
|
- new = "Summary: newer\n\nPrior Details:\n- Sense could find prey.\n"
|
|
|
|
|
- service, emitted, _, _, _ = _triage_service(brief_fn=lambda prompt: new)
|
|
|
|
|
- service.last_triage = {**_escalated_payload(), "brief": prior}
|
|
|
|
|
- service._pending_brief = ["Unknown: more"]
|
|
|
|
|
|
|
+def test_ledger_records_fact_and_survives_model_reemit():
|
|
|
|
|
+ """A recorded fact persists even when a later reply omits it."""
|
|
|
|
|
+ first = _brief_reply("Summary: rolling", ["The budget was $305 million."])
|
|
|
|
|
+ second = _brief_reply("Summary: newer", [])
|
|
|
|
|
+ replies = iter([first, second])
|
|
|
|
|
+ service, _, _, _, _ = _triage_service(brief_fn=lambda prompt: next(replies))
|
|
|
|
|
+
|
|
|
|
|
+ service._pending_brief = ["Unknown: one"]
|
|
|
service._brief_outstanding = True
|
|
service._brief_outstanding = True
|
|
|
|
|
+ service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
|
|
|
|
|
|
|
|
+ service._pending_brief = ["Unknown: two"]
|
|
|
|
|
+ service._brief_outstanding = True
|
|
|
service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
|
|
|
|
|
|
assert service.last_triage is not None
|
|
assert service.last_triage is not None
|
|
|
brief = service.last_triage["brief"]
|
|
brief = service.last_triage["brief"]
|
|
|
- assert "- Sense could find prey." in brief
|
|
|
|
|
- assert "- The budget was $305 million." in brief
|
|
|
|
|
- assert brief.count("- Sense could find prey.") == 1
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def test_retention_guard_leaves_complete_brief_unchanged():
|
|
|
|
|
- """A brief that already carries every prior bullet is stored verbatim."""
|
|
|
|
|
- prior = "Prior Details:\n- fact one\n- fact two\n"
|
|
|
|
|
- new = "Summary: newer\n\nPrior Details:\n- fact one\n- fact two\n"
|
|
|
|
|
- service, _, _, _, _ = _triage_service(brief_fn=lambda prompt: new)
|
|
|
|
|
- service.last_triage = {**_escalated_payload(), "brief": prior}
|
|
|
|
|
- service._pending_brief = ["Unknown: more"]
|
|
|
|
|
|
|
+ assert "The budget was $305 million." in brief
|
|
|
|
|
+ assert service._fact_ledger.facts() == ["The budget was $305 million."]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_ledger_dedupes_repeated_fact():
|
|
|
|
|
+ """The same fact proposed twice is recorded once."""
|
|
|
|
|
+ reply = _brief_reply("Summary: s", ["Sense could find prey.", "sense could find prey"])
|
|
|
|
|
+ service, _, _, _, _ = _triage_service(brief_fn=lambda prompt: reply)
|
|
|
|
|
+ service._pending_brief = ["Unknown: one"]
|
|
|
service._brief_outstanding = True
|
|
service._brief_outstanding = True
|
|
|
|
|
|
|
|
service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
|
|
|
|
|
|
- assert service.last_triage is not None
|
|
|
|
|
- assert service.last_triage["brief"] == new
|
|
|
|
|
|
|
+ assert service._fact_ledger.facts() == ["Sense could find prey."]
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_pinned_prompt_carries_prior_brief_and_new_utterances():
|
|
|
|
|
- """The brief request hands the model the pinned current brief + new deltas."""
|
|
|
|
|
|
|
+def test_ledger_prompt_carries_ledger_and_narrative():
|
|
|
|
|
+ """The brief request hands the model the ledger, narrative, and new deltas."""
|
|
|
seen: list[str] = []
|
|
seen: list[str] = []
|
|
|
service, _, _, _, _ = _triage_service(
|
|
service, _, _, _, _ = _triage_service(
|
|
|
- brief_fn=lambda prompt: seen.append(prompt) or "UPDATED"
|
|
|
|
|
|
|
+ brief_fn=lambda prompt: seen.append(prompt) or _brief_reply("UPDATED")
|
|
|
)
|
|
)
|
|
|
- service.last_triage = {**_escalated_payload(), "brief": "PRIOR BRIEF TEXT"}
|
|
|
|
|
|
|
+ service._fact_ledger.merge(["PRIOR FACT"])
|
|
|
|
|
+ service._last_brief_narrative = "PRIOR NARRATIVE"
|
|
|
service._pending_brief = ["Unknown: hello there"]
|
|
service._pending_brief = ["Unknown: hello there"]
|
|
|
service._brief_outstanding = True
|
|
service._brief_outstanding = True
|
|
|
|
|
|
|
|
service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
service._run_brief_update(0, _escalated_payload(), service._now(), service._brief_token)
|
|
|
|
|
|
|
|
assert len(seen) == 1
|
|
assert len(seen) == 1
|
|
|
- assert seen[0].startswith("CURRENT BRIEF:\nPRIOR BRIEF TEXT")
|
|
|
|
|
|
|
+ assert "CURRENT LEDGER:" in seen[0]
|
|
|
|
|
+ assert "- PRIOR FACT" in seen[0]
|
|
|
|
|
+ assert "CURRENT BRIEF:\nPRIOR NARRATIVE" in seen[0]
|
|
|
assert "NEW UTTERANCES:\n- Unknown: hello there" in seen[0]
|
|
assert "NEW UTTERANCES:\n- Unknown: hello there" in seen[0]
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1547,7 +1582,6 @@ def test_brief_merges_content_and_writes_file():
|
|
|
assert service._pending_brief == []
|
|
assert service._pending_brief == []
|
|
|
assert service._brief_outstanding is False
|
|
assert service._brief_outstanding is False
|
|
|
|
|
|
|
|
-
|
|
|
|
|
def test_empty_brief_requeues_deltas():
|
|
def test_empty_brief_requeues_deltas():
|
|
|
"""A `""` reply keeps signals, re-buffers the deltas, and clears the flag."""
|
|
"""A `""` reply keeps signals, re-buffers the deltas, and clears the flag."""
|
|
|
service, emitted, _, _, _ = _triage_service(brief_fn=lambda prompt: "")
|
|
service, emitted, _, _, _ = _triage_service(brief_fn=lambda prompt: "")
|
|
@@ -1648,11 +1682,11 @@ def test_deltas_arriving_during_call_survive_and_flush():
|
|
|
|
|
|
|
|
appended: list[int] = []
|
|
appended: list[int] = []
|
|
|
|
|
|
|
|
- def _brief(prompt):
|
|
|
|
|
|
|
+ def _brief(prompt, on_io=None, finalize=None):
|
|
|
if not appended:
|
|
if not appended:
|
|
|
appended.append(1)
|
|
appended.append(1)
|
|
|
service.handle_event(_transcript(clock, "during call"))
|
|
service.handle_event(_transcript(clock, "during call"))
|
|
|
- return "first brief"
|
|
|
|
|
|
|
+ return _brief_reply("first brief")
|
|
|
|
|
|
|
|
service._brief_fn = _brief
|
|
service._brief_fn = _brief
|
|
|
service._run_brief_update(0, signals, clock(), token)
|
|
service._run_brief_update(0, signals, clock(), token)
|
|
@@ -2111,12 +2145,12 @@ def test_tick_heals_triage_panel_after_lane_ready():
|
|
|
lane.stop()
|
|
lane.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
-def _dump_brief(prompt, on_io=None):
|
|
|
|
|
|
|
+def _dump_brief(prompt, on_io=None, finalize=None):
|
|
|
"""Brief fake that mimics LlmClient.chat's on_io phases."""
|
|
"""Brief fake that mimics LlmClient.chat's on_io phases."""
|
|
|
if on_io is not None:
|
|
if on_io is not None:
|
|
|
on_io({"phase": "request", "url": "http://llm/v1", "body": {"prompt": prompt}})
|
|
on_io({"phase": "request", "url": "http://llm/v1", "body": {"prompt": prompt}})
|
|
|
on_io({"phase": "response", "payload": {"content": "parsed"}, "error": None})
|
|
on_io({"phase": "response", "payload": {"content": "parsed"}, "error": None})
|
|
|
- return "BRIEF"
|
|
|
|
|
|
|
+ return _brief_reply("BRIEF")
|
|
|
|
|
|
|
|
|
|
|
|
|
def _run_triage_flow(service, emitted, clock, lane, payload):
|
|
def _run_triage_flow(service, emitted, clock, lane, payload):
|
|
@@ -2157,7 +2191,8 @@ def test_dump_writes_full_laya_and_llm_io(tmp_path):
|
|
|
assert data["laya"]["error"] is None
|
|
assert data["laya"]["error"] is None
|
|
|
assert data["llm"]["request"]["url"] == "http://llm/v1"
|
|
assert data["llm"]["request"]["url"] == "http://llm/v1"
|
|
|
assert data["llm"]["request"]["body"] == {
|
|
assert data["llm"]["request"]["body"] == {
|
|
|
- "prompt": "CURRENT BRIEF:\n(none yet)\n\nNEW UTTERANCES:\n- Unknown: hello"
|
|
|
|
|
|
|
+ "prompt": "CURRENT LEDGER:\n(none yet)\n\nCURRENT BRIEF:\n(none yet)\n\n"
|
|
|
|
|
+ "NEW UTTERANCES:\n- Unknown: hello"
|
|
|
}
|
|
}
|
|
|
assert data["llm"]["response"] == {"content": "parsed"}
|
|
assert data["llm"]["response"] == {"content": "parsed"}
|
|
|
assert data["llm"]["error"] is None
|
|
assert data["llm"]["error"] is None
|
|
@@ -2283,13 +2318,13 @@ def test_dump_brief_cleared_mid_flight_writes_nothing(tmp_path):
|
|
|
service._triage_dump = TriageDumper(True, directory=str(tmp_path))
|
|
service._triage_dump = TriageDumper(True, directory=str(tmp_path))
|
|
|
service.cfg.triage.dump_io = True
|
|
service.cfg.triage.dump_io = True
|
|
|
|
|
|
|
|
- def stale_brief(prompt, on_io=None):
|
|
|
|
|
|
|
+ def stale_brief(prompt, on_io=None, finalize=None):
|
|
|
# Simulate a clear landing mid-LLM-call (after the request phase fired).
|
|
# Simulate a clear landing mid-LLM-call (after the request phase fired).
|
|
|
service._triage_generation += 1
|
|
service._triage_generation += 1
|
|
|
if on_io is not None:
|
|
if on_io is not None:
|
|
|
on_io({"phase": "request", "url": "http://llm/v1", "body": {}})
|
|
on_io({"phase": "request", "url": "http://llm/v1", "body": {}})
|
|
|
on_io({"phase": "response", "payload": {"c": 1}, "error": None})
|
|
on_io({"phase": "response", "payload": {"c": 1}, "error": None})
|
|
|
- return "RAW"
|
|
|
|
|
|
|
+ return _brief_reply("RAW")
|
|
|
|
|
|
|
|
service._brief_fn = stale_brief
|
|
service._brief_fn = stale_brief
|
|
|
service._pending_brief = ["Unknown: hello"]
|
|
service._pending_brief = ["Unknown: hello"]
|
|
@@ -2348,14 +2383,14 @@ def test_dump_coalesced_update_writes_laya_only_then_llm_only_flush(tmp_path):
|
|
|
|
|
|
|
|
appended: list[int] = []
|
|
appended: list[int] = []
|
|
|
|
|
|
|
|
- def _brief(prompt, on_io=None):
|
|
|
|
|
|
|
+ def _brief(prompt, on_io=None, finalize=None):
|
|
|
if not appended:
|
|
if not appended:
|
|
|
appended.append(1)
|
|
appended.append(1)
|
|
|
service.handle_event(_transcript(clock, "during call"))
|
|
service.handle_event(_transcript(clock, "during call"))
|
|
|
if on_io is not None:
|
|
if on_io is not None:
|
|
|
on_io({"phase": "request", "url": "http://llm/v1", "body": {"prompt": prompt}})
|
|
on_io({"phase": "request", "url": "http://llm/v1", "body": {"prompt": prompt}})
|
|
|
on_io({"phase": "response", "payload": {"content": "parsed"}, "error": None})
|
|
on_io({"phase": "response", "payload": {"content": "parsed"}, "error": None})
|
|
|
- return "BRIEF"
|
|
|
|
|
|
|
+ return _brief_reply("BRIEF")
|
|
|
|
|
|
|
|
service._brief_fn = _brief
|
|
service._brief_fn = _brief
|
|
|
clock.advance(1.0)
|
|
clock.advance(1.0)
|
|
@@ -2380,7 +2415,8 @@ def test_dump_coalesced_update_writes_laya_only_then_llm_only_flush(tmp_path):
|
|
|
assert len(both) == 1
|
|
assert len(both) == 1
|
|
|
assert coalesced[0]["ts"] != flushed_records[0]["ts"]
|
|
assert coalesced[0]["ts"] != flushed_records[0]["ts"]
|
|
|
assert flushed_records[0]["llm"]["request"]["body"] == {
|
|
assert flushed_records[0]["llm"]["request"]["body"] == {
|
|
|
- "prompt": "CURRENT BRIEF:\nBRIEF\n\nNEW UTTERANCES:\n- Unknown: during call"
|
|
|
|
|
|
|
+ "prompt": "CURRENT LEDGER:\n(none yet)\n\nCURRENT BRIEF:\nBRIEF\n\n"
|
|
|
|
|
+ "NEW UTTERANCES:\n- Unknown: during call"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|