Falln87 commited on
Commit
9dae931
·
verified ·
1 Parent(s): 61cccdd

generator: temporal answer uses slot-held value; gold ADD evicts first when ledger full; budget default 12 for eviction pressure

Browse files
Files changed (1) hide show
  1. clerk/generator.py +34 -11
clerk/generator.py CHANGED
@@ -15,6 +15,8 @@ Invariants the code maintains:
15
  * n_uses (the salience signal) is incremented at the moment a question
16
  is asked, so the eviction oracle at session t only sees evidence
17
  available up to t.
 
 
18
  * Everything is seeded: the same --seed yields byte-identical data.
19
 
20
  Author: Justin Wolcott (fallnai-research.org)
@@ -210,7 +212,7 @@ def render_session(
210
  qas.append({"q": q, "a": ANSWER_UNKNOWN, "type": "unknown",
211
  "pred": spec["p"], "value": None})
212
  elif f["state"] == "tombstoned":
213
- old = f["history"][-1]["value"] if f["history"] else None
214
  if old and rng.random() < 0.5:
215
  q2 = rng.choice(Q_TEMPLATES_TEMPORAL).replace("{h}", spec["h"])
216
  qas.append({"q": q2, "a": ANSWER_TEMPORAL.replace("{v}", old),
@@ -267,6 +269,10 @@ def _slot_id_for(ledger: List[Slot], pending: List[Dict[str, Any]],
267
  return None
268
 
269
 
 
 
 
 
270
  def gold_ops_for_events(
271
  ledger: List[Slot],
272
  persona_name: str,
@@ -276,21 +282,36 @@ def gold_ops_for_events(
276
  """Build the gold edit program for one session's events, in transcript
277
  order, then apply budget pressure with the salience oracle.
278
 
279
- Eviction-safe: if a predicate's slot was already evicted, an UPDATE
280
- degrades to an ADD (the fact must be re-remembered) and a TOMBSTONE of
281
- an absent slot is skipped."""
 
282
  ops: List[Dict[str, Any]] = []
283
  ledger_now = [Slot(**vars(s)) for s in ledger]
 
 
 
 
 
284
  for ev in events:
285
- if ev["kind"] == "ADD":
 
 
 
 
 
 
 
 
 
 
 
 
286
  op = {"op": "ADD", "s": persona_name, "p": ev["pred"], "o": ev["value"]}
287
  elif ev["kind"] == "UPDATE":
288
  sid = _slot_id_for(ledger_now, ops, persona_name, ev["pred"])
289
- if sid is None:
290
- op = {"op": "ADD", "s": persona_name, "p": ev["pred"], "o": ev["value"]}
291
- else:
292
- op = {"op": "UPDATE", "i": sid, "s": persona_name,
293
- "p": ev["pred"], "o": ev["value"]}
294
  else: # TOMBSTONE
295
  sid = _slot_id_for(ledger_now, ops, persona_name, ev["pred"])
296
  if sid is None:
@@ -363,7 +384,9 @@ def main() -> None:
363
  ap = argparse.ArgumentParser(description="CLERK synthetic session generator")
364
  ap.add_argument("--personas", type=int, default=100)
365
  ap.add_argument("--sessions", type=int, default=8)
366
- ap.add_argument("--budget", type=int, default=LEDGER_BUDGET_DEFAULT)
 
 
367
  ap.add_argument("--seed", type=int, default=137)
368
  ap.add_argument("--split", choices=["train", "test"], default="train")
369
  ap.add_argument("--out", type=str, required=True)
 
15
  * n_uses (the salience signal) is incremented at the moment a question
16
  is asked, so the eviction oracle at session t only sees evidence
17
  available up to t.
18
+ * Gold programs are always executable: if an ADD would overflow a full
19
+ ledger, the oracle emits EVICT (lowest salience) first, then the ADD.
20
  * Everything is seeded: the same --seed yields byte-identical data.
21
 
22
  Author: Justin Wolcott (fallnai-research.org)
 
212
  qas.append({"q": q, "a": ANSWER_UNKNOWN, "type": "unknown",
213
  "pred": spec["p"], "value": None})
214
  elif f["state"] == "tombstoned":
215
+ old = f["value"] # value the tombstoned slot holds
216
  if old and rng.random() < 0.5:
217
  q2 = rng.choice(Q_TEMPLATES_TEMPORAL).replace("{h}", spec["h"])
218
  qas.append({"q": q2, "a": ANSWER_TEMPORAL.replace("{v}", old),
 
269
  return None
270
 
271
 
272
+ def _free_slot(sim: List[Slot]) -> Optional[Slot]:
273
+ return next((s for s in sim if not s.occupied), None)
274
+
275
+
276
  def gold_ops_for_events(
277
  ledger: List[Slot],
278
  persona_name: str,
 
282
  """Build the gold edit program for one session's events, in transcript
283
  order, then apply budget pressure with the salience oracle.
284
 
285
+ Always-executable: if an ADD would overflow a full ledger, the program
286
+ first EVICTs the lowest-salience occupied slot, then ADDs. If a
287
+ predicate's slot was already evicted, an UPDATE degrades to an ADD and a
288
+ TOMBSTONE of an absent slot is skipped."""
289
  ops: List[Dict[str, Any]] = []
290
  ledger_now = [Slot(**vars(s)) for s in ledger]
291
+
292
+ def sim_after_pending() -> List[Slot]:
293
+ s, _ = apply_ops(ledger_now, ops, session_idx)
294
+ return s
295
+
296
  for ev in events:
297
+ if ev["kind"] in ("ADD", "UPDATE degraded to ADD"):
298
+ pass # fallthrough handled below
299
+ if ev["kind"] == "ADD" or (ev["kind"] == "UPDATE" and
300
+ _slot_id_for(ledger_now, ops, persona_name,
301
+ ev["pred"]) is None):
302
+ # evict-first if the ledger is full
303
+ if _free_slot(sim_after_pending()) is None:
304
+ cur, _ = apply_ops(ledger_now, ops, session_idx)
305
+ victim = min((s for s in cur if s.occupied),
306
+ key=lambda s: salience(s, session_idx))
307
+ evict = {"op": "EVICT", "i": victim.id}
308
+ ops.append(evict)
309
+ ledger_now, _ = apply_ops(ledger_now, [evict], session_idx)
310
  op = {"op": "ADD", "s": persona_name, "p": ev["pred"], "o": ev["value"]}
311
  elif ev["kind"] == "UPDATE":
312
  sid = _slot_id_for(ledger_now, ops, persona_name, ev["pred"])
313
+ op = {"op": "UPDATE", "i": sid, "s": persona_name,
314
+ "p": ev["pred"], "o": ev["value"]}
 
 
 
315
  else: # TOMBSTONE
316
  sid = _slot_id_for(ledger_now, ops, persona_name, ev["pred"])
317
  if sid is None:
 
384
  ap = argparse.ArgumentParser(description="CLERK synthetic session generator")
385
  ap.add_argument("--personas", type=int, default=100)
386
  ap.add_argument("--sessions", type=int, default=8)
387
+ ap.add_argument("--budget", type=int, default=12,
388
+ help="ledger budget; 12 guarantees eviction pressure "
389
+ "(personas accumulate up to ~18 facts)")
390
  ap.add_argument("--seed", type=int, default=137)
391
  ap.add_argument("--split", choices=["train", "test"], default="train")
392
  ap.add_argument("--out", type=str, required=True)