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

generator: remove module-global session index; question targets restricted to ledger-supported predicates

Browse files
Files changed (1) hide show
  1. clerk/generator.py +24 -40
clerk/generator.py CHANGED
@@ -10,11 +10,11 @@ Invariants the code maintains:
10
  * Questions are only asked about predicates the ledger can actually
11
  support: never-stated (unknown), currently-valid (stale/superseded),
12
  or currently-tombstoned (negated/temporal). Active-but-EVICTED
13
- predicates are never queried, so supervision never asks the model to
14
- recall what the oracle dropped.
15
- * n_uses (the salience signal) is tracked incrementally at the moment a
16
- question is asked, so the eviction oracle at session t only sees
17
- evidence available up to t.
18
  * Everything is seeded: the same --seed yields byte-identical data.
19
 
20
  Author: Justin Wolcott (fallnai-research.org)
@@ -33,14 +33,13 @@ from clerk.common import (
33
  Slot,
34
  apply_ops,
35
  empty_ledger,
36
- enforce_budget,
37
- parse_ledger,
38
  serialize_ledger,
39
  )
40
 
41
  # ---------------------------------------------------------------- predicates
42
  # p: ledger key | h: human phrase | v: value pool
43
- # add/upd/tmb: statement templates. {v} = new value. {v_old} = previous value.
44
 
45
  PREDICATES: List[Dict[str, Any]] = [
46
  {"p": "job", "h": "job", "v": ["barista", "chef", "nurse", "high school teacher", "librarian", "electrician", "accountant", "software engineer", "pharmacist", "bus driver"],
@@ -146,16 +145,16 @@ def render_session(
146
  rng: random.Random,
147
  persona: Dict[str, Any],
148
  ledger_before: List[Slot],
 
149
  n_add: int, n_upd: int, n_tmb: int, n_q: int,
150
  ) -> Tuple[List[Dict[str, str]], List[Dict[str, Any]], List[Dict[str, Any]]]:
151
  """Render one session. Returns (turns, write_events, qas).
152
 
153
- Question targets are constrained by the ledger the assistant will read
154
- from (ledger_before, i.e. end of the previous session):
155
- predicate absent from persona facts ....... unknown
156
- valid slot in ledger ....................... stale / superseded
157
- tombstoned slot in ledger .................. negated / temporal
158
- active in persona but EVICTED from ledger .. never asked
159
  """
160
  turns: List[Dict[str, str]] = []
161
  qas: List[Dict[str, Any]] = []
@@ -165,7 +164,7 @@ def render_session(
165
  turns.append({"role": "assistant",
166
  "content": rng.choice(ACKS) if assistant is None else assistant})
167
 
168
- # ---- events (chosen against persona state at session start)
169
  events: List[Dict[str, Any]] = []
170
 
171
  for _ in range(n_add):
@@ -179,7 +178,7 @@ def render_session(
179
 
180
  for _ in range(n_upd):
181
  updatable = [f for f in persona["facts"]
182
- if f["state"] == "active" and f["born_session"] < _session_being_rendered]
183
  if not updatable:
184
  break
185
  f = rng.choice(updatable)
@@ -199,20 +198,10 @@ def render_session(
199
 
200
  rng.shuffle(events)
201
 
202
- # ---- questions, asked before consolidation
203
- led_state: Dict[str, Tuple[bool, bool]] = {} # pred -> (in_persona, in_ledger_valid)
204
- for s in ledger_before:
205
- if s.occupied:
206
- led_state[s.predicate] = (True, s.valid)
207
- for f in persona["facts"]:
208
- if f["pred"] not in led_state:
209
- # active but never consolidated (session 0) or evicted
210
- led_state[f["pred"]] = (True, f["state"] != "tombstoned")
211
-
212
  askable = [p for p in PREDICATES
213
- if p["p"] not in led_state # unknown questions
214
- or led_state[p["p"]][1] is True # stale/superseded
215
- or not led_state[p["p"]][1]] # negated/temporal
216
  rng.shuffle(askable)
217
  for spec in askable[:n_q]:
218
  f = find_fact(persona, spec["p"])
@@ -229,9 +218,12 @@ def render_session(
229
  else:
230
  qas.append({"q": q, "a": ANSWER_NEGATED, "type": "negated",
231
  "pred": spec["p"], "value": None})
232
- else:
233
  qas.append({"q": q, "a": f["value"], "type": "superseded",
234
  "pred": spec["p"], "value": f["value"]})
 
 
 
235
 
236
  # ---- render turns: statements (+ chatter), then the Q&A block
237
  n_chatter = rng.randint(1, 3)
@@ -246,9 +238,6 @@ def render_session(
246
  return turns, events, qas
247
 
248
 
249
- _session_being_rendered: int = 0
250
-
251
-
252
  def apply_event_to_persona(persona: Dict[str, Any], ev: Dict[str, Any],
253
  session_idx: int) -> None:
254
  if ev["kind"] == "ADD":
@@ -285,7 +274,7 @@ def gold_ops_for_events(
285
  session_idx: int,
286
  ) -> Tuple[List[Dict[str, Any]], List[Slot]]:
287
  """Build the gold edit program for one session's events, in transcript
288
- order, applying budget pressure with the salience oracle afterwards.
289
 
290
  Eviction-safe: if a predicate's slot was already evicted, an UPDATE
291
  degrades to an ADD (the fact must be re-remembered) and a TOMBSTONE of
@@ -310,10 +299,8 @@ def gold_ops_for_events(
310
  ops.append(op)
311
  ledger_now, _ = apply_ops(ledger_now, [op], session_idx)
312
 
313
- # budget pressure: evict lowest-salience occupied slots until it fits
314
  occupied = [s for s in ledger_now if s.occupied]
315
  for _ in range(max(0, len(occupied) - len(ledger_now))):
316
- from clerk.common import salience
317
  victim = min((s for s in ledger_now if s.occupied),
318
  key=lambda s: salience(s, session_idx))
319
  ops.append({"op": "EVICT", "i": victim.id})
@@ -331,9 +318,6 @@ def generate_persona_timeline(
331
  sessions: List[Dict[str, Any]] = []
332
 
333
  for s_idx in range(n_sessions):
334
- global _session_being_rendered
335
- _session_being_rendered = s_idx
336
-
337
  if s_idx == 0:
338
  # intro session: persona states the initial facts
339
  events = [{"kind": "ADD", "pred": f["pred"], "value": f["value"],
@@ -347,7 +331,7 @@ def generate_persona_timeline(
347
  qas: List[Dict[str, Any]] = []
348
  else:
349
  turns, events, qas = render_session(
350
- rng, persona, ledger,
351
  rng.randint(1, 2), rng.randint(0, 2), rng.randint(0, 1),
352
  rng.randint(1, 3))
353
  for ev in events:
 
10
  * Questions are only asked about predicates the ledger can actually
11
  support: never-stated (unknown), currently-valid (stale/superseded),
12
  or currently-tombstoned (negated/temporal). Active-but-EVICTED
13
+ predicates are never asked, so supervision never demands recall of
14
+ what the memory no longer holds.
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)
 
33
  Slot,
34
  apply_ops,
35
  empty_ledger,
36
+ salience,
 
37
  serialize_ledger,
38
  )
39
 
40
  # ---------------------------------------------------------------- predicates
41
  # p: ledger key | h: human phrase | v: value pool
42
+ # add/upd/tmb: statement templates ({v} = new value).
43
 
44
  PREDICATES: List[Dict[str, Any]] = [
45
  {"p": "job", "h": "job", "v": ["barista", "chef", "nurse", "high school teacher", "librarian", "electrician", "accountant", "software engineer", "pharmacist", "bus driver"],
 
145
  rng: random.Random,
146
  persona: Dict[str, Any],
147
  ledger_before: List[Slot],
148
+ session_idx: int,
149
  n_add: int, n_upd: int, n_tmb: int, n_q: int,
150
  ) -> Tuple[List[Dict[str, str]], List[Dict[str, Any]], List[Dict[str, Any]]]:
151
  """Render one session. Returns (turns, write_events, qas).
152
 
153
+ Question targets are constrained to what the ledger supports:
154
+ predicate never stated .................... unknown
155
+ predicate has a slot in ledger (v=1) ...... stale / superseded
156
+ predicate has a tombstoned slot (v=0) ..... negated / temporal
157
+ predicate active but EVICTED from ledger .. never asked
 
158
  """
159
  turns: List[Dict[str, str]] = []
160
  qas: List[Dict[str, Any]] = []
 
164
  turns.append({"role": "assistant",
165
  "content": rng.choice(ACKS) if assistant is None else assistant})
166
 
167
+ # ---- write events (chosen against persona state at session start)
168
  events: List[Dict[str, Any]] = []
169
 
170
  for _ in range(n_add):
 
178
 
179
  for _ in range(n_upd):
180
  updatable = [f for f in persona["facts"]
181
+ if f["state"] == "active" and f["born_session"] < session_idx]
182
  if not updatable:
183
  break
184
  f = rng.choice(updatable)
 
198
 
199
  rng.shuffle(events)
200
 
201
+ # ---- questions, answered from ledger_before (pre-consolidation state)
202
+ in_ledger = {s.predicate for s in ledger_before if s.occupied}
 
 
 
 
 
 
 
 
203
  askable = [p for p in PREDICATES
204
+ if find_fact(persona, p["p"]) is None or p["p"] in in_ledger]
 
 
205
  rng.shuffle(askable)
206
  for spec in askable[:n_q]:
207
  f = find_fact(persona, spec["p"])
 
218
  else:
219
  qas.append({"q": q, "a": ANSWER_NEGATED, "type": "negated",
220
  "pred": spec["p"], "value": None})
221
+ elif f["state"] == "updated":
222
  qas.append({"q": q, "a": f["value"], "type": "superseded",
223
  "pred": spec["p"], "value": f["value"]})
224
+ else:
225
+ qas.append({"q": q, "a": f["value"], "type": "stale",
226
+ "pred": spec["p"], "value": f["value"]})
227
 
228
  # ---- render turns: statements (+ chatter), then the Q&A block
229
  n_chatter = rng.randint(1, 3)
 
238
  return turns, events, qas
239
 
240
 
 
 
 
241
  def apply_event_to_persona(persona: Dict[str, Any], ev: Dict[str, Any],
242
  session_idx: int) -> None:
243
  if ev["kind"] == "ADD":
 
274
  session_idx: int,
275
  ) -> Tuple[List[Dict[str, Any]], List[Slot]]:
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
 
299
  ops.append(op)
300
  ledger_now, _ = apply_ops(ledger_now, [op], session_idx)
301
 
 
302
  occupied = [s for s in ledger_now if s.occupied]
303
  for _ in range(max(0, len(occupied) - len(ledger_now))):
 
304
  victim = min((s for s in ledger_now if s.occupied),
305
  key=lambda s: salience(s, session_idx))
306
  ops.append({"op": "EVICT", "i": victim.id})
 
318
  sessions: List[Dict[str, Any]] = []
319
 
320
  for s_idx in range(n_sessions):
 
 
 
321
  if s_idx == 0:
322
  # intro session: persona states the initial facts
323
  events = [{"kind": "ADD", "pred": f["pred"], "value": f["value"],
 
331
  qas: List[Dict[str, Any]] = []
332
  else:
333
  turns, events, qas = render_session(
334
+ rng, persona, ledger, s_idx,
335
  rng.randint(1, 2), rng.randint(0, 2), rng.randint(0, 1),
336
  rng.randint(1, 3))
337
  for ev in events: