Instructions to use Falln87/clerk-memory with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use Falln87/clerk-memory with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
File size: 8,271 Bytes
4f70a26 a042d28 4f70a26 a042d28 4f70a26 a042d28 4f70a26 a042d28 4f70a26 a042d28 4f70a26 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | """CLERK: Consolidated Ledger with Eviction and Rewrite Keys.
Core memory machinery shared by the data generator, the training harness and
the evaluators:
* ``Slot`` -- one memory slot (a typed fact plus lifecycle metadata)
* edit-op schema -- ADD / UPDATE / TOMBSTONE / EVICT / NOOP
* ``apply_ops`` -- the DETERMINISTIC reducer that applies a model-emitted
edit program to a ledger
* serializers -- compact JSON forms of ledgers and op programs
The reducer is the single source of truth: the generator emits gold op
programs, and every downstream consumer applies them through ``apply_ops``.
This guarantees that the training supervision, the simulated model rollouts
and the evaluation all execute the exact same state transition.
Author: Justin Wolcott (fallnai-research.org)
License: Apache-2.0
"""
from __future__ import annotations
import json
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
LEDGER_BUDGET_DEFAULT = 24
VALID_OPS = ("ADD", "UPDATE", "TOMBSTONE", "EVICT", "NOOP")
@dataclass
class Slot:
"""A single ledger slot."""
id: int
subject: str = ""
predicate: str = ""
object: str = ""
t_last: int = -1 # session index when this slot was last written
n_uses: int = 0 # number of times the fact has been queried (internal)
valid: bool = True # False => tombstoned (fact no longer true)
occupied: bool = False # False => free (empty, or freed by EVICT)
def empty_ledger(budget: int = LEDGER_BUDGET_DEFAULT) -> List[Slot]:
return [Slot(id=i) for i in range(budget)]
def serialize_ledger(ledger: List[Slot]) -> str:
"""Compact JSON view of a ledger, as seen by the model.
Tombstoned slots stay visible (with v=0) until they are evicted, so
temporal questions about superseded values remain answerable.
"""
rows = []
for s in ledger:
if s.occupied:
rows.append(
{"i": s.id, "s": s.subject, "p": s.predicate,
"o": s.object, "t": s.t_last, "v": 1 if s.valid else 0}
)
return json.dumps(rows, separators=(",", ":"), ensure_ascii=False)
def parse_ledger(text: str, budget: int = LEDGER_BUDGET_DEFAULT) -> List[Slot]:
"""Rebuild a ledger from its serialized form."""
ledger = empty_ledger(budget)
for row in json.loads(text):
s = ledger[row["i"]]
s.occupied = True
s.subject = row["s"]
s.predicate = row["p"]
s.object = row["o"]
s.t_last = row["t"]
s.valid = bool(row["v"])
return ledger
def parse_ops(text: str) -> Tuple[List[Dict[str, Any]], str]:
"""Parse a model-emitted edit program.
Returns (ops, error). ``error`` is "" on success, otherwise a short
description of the first problem found. Malformed programs are surfaced
to the evaluation harness instead of being silently dropped.
"""
text = text.strip()
if text.startswith("```"):
text = text.strip("`")
if text.startswith("json"):
text = text[4:]
try:
payload = json.loads(text)
except json.JSONDecodeError as e:
return [], f"json_decode_error: {e}"
if not isinstance(payload, dict) or "ops" not in payload:
return [], "missing_top_level_ops_key"
ops = payload["ops"]
if not isinstance(ops, list):
return [], "ops_not_a_list"
clean: List[Dict[str, Any]] = []
for op in ops:
if not isinstance(op, dict) or "op" not in op:
return clean, "op_missing_op_key"
if op["op"] not in VALID_OPS:
return clean, f"unknown_op:{op['op']}"
clean.append(op)
return clean, ""
def apply_ops(
ledger: List[Slot],
ops: List[Dict[str, Any]],
session_idx: int,
) -> Tuple[List[Slot], Dict[str, int]]:
"""Apply an edit program to a ledger. Deterministic; mutates a copy.
Rules:
ADD -- write into the lowest free slot; if the ledger has no free
slot the op is DROPPED and counted (a trained policy must
EVICT first; the gold generator always does).
UPDATE -- overwrite s/p/o of slot ``i`` and bump t_last. The slot
keeps its identity, so supersession is explicit.
TOMBSTONE -- mark slot ``i`` invalid (v=0); content stays until EVICT.
EVICT -- free slot ``i`` entirely.
NOOP -- nothing.
"""
new_ledger = [Slot(**vars(s)) for s in ledger]
stats = {"ADD": 0, "UPDATE": 0, "TOMBSTONE": 0, "EVICT": 0,
"NOOP": 0, "dropped": 0}
for op in ops:
kind = op["op"]
stats[kind] += 1
if kind == "NOOP":
continue
if kind == "ADD":
target = next((s for s in new_ledger if not s.occupied), None)
if target is None:
stats["dropped"] += 1
continue
target.occupied = True
target.valid = True
target.t_last = session_idx
target.n_uses = 0
target.subject = str(op.get("s", ""))
target.predicate = str(op.get("p", ""))
target.object = str(op.get("o", ""))
elif kind == "UPDATE":
i = int(op.get("i", -1))
if not (0 <= i < len(new_ledger)) or not new_ledger[i].occupied:
stats["dropped"] += 1
continue
s = new_ledger[i]
if op.get("s") is not None:
s.subject = str(op["s"])
if op.get("p") is not None:
s.predicate = str(op["p"])
s.object = str(op.get("o", s.object))
s.t_last = session_idx
s.valid = True
elif kind == "TOMBSTONE":
i = int(op.get("i", -1))
if not (0 <= i < len(new_ledger)) or not new_ledger[i].occupied:
stats["dropped"] += 1
continue
new_ledger[i].valid = False
elif kind == "EVICT":
i = int(op.get("i", -1))
if not (0 <= i < len(new_ledger)) or not new_ledger[i].occupied:
stats["dropped"] += 1
continue
new_ledger[i] = Slot(id=i)
return new_ledger, stats
def salience(slot: Slot, now: int) -> float:
"""Gold salience score used by the generator's eviction oracle.
Frequently-queried, currently-valid, recently-written facts are kept;
old, never-queried, tombstoned facts are evicted first. This is a
documented oracle, not a model component: the generator needs a
deterministic ground truth for eviction supervision.
"""
age = max(0, now - slot.t_last)
return 4.0 * slot.n_uses + (3.0 if slot.valid else 0.0) + max(0.0, 4.0 - age)
def enforce_budget(
ledger: List[Slot], ops: List[Dict[str, Any]], now: int
) -> Tuple[List[Slot], List[Dict[str, Any]]]:
"""Apply ops, then evict lowest-salience occupied slots until the
occupied count fits the budget (== number of slots). Appends EVICT ops
to the program so the policy learns budget-aware behavior."""
ledger, _ = apply_ops(ledger, ops, now)
ops = list(ops)
occupied = [s for s in ledger if s.occupied]
for _ in range(max(0, len(occupied) - len(ledger))):
victim = min((s for s in ledger if s.occupied),
key=lambda s: salience(s, now))
ops.append({"op": "EVICT", "i": victim.id})
ledger[victim.id] = Slot(id=victim.id)
return ledger, ops
def facts_of(ledger: List[Slot], valid_only: bool = True) -> Dict[Tuple[str, str], str]:
"""Current fact map {(subject, predicate): object}.
valid_only=True keeps only active slots; False also returns tombstoned
slots (last-known value), which backs temporal questions."""
out: Dict[Tuple[str, str], str] = {}
for s in ledger:
if s.occupied and (s.valid or not valid_only):
out[(s.subject, s.predicate)] = s.object
return out
def ledger_stats(ledger: List[Slot]) -> Dict[str, int]:
occ = [s for s in ledger if s.occupied]
return {
"occupied": len(occ),
"valid": sum(1 for s in occ if s.valid),
"tombstoned": sum(1 for s in occ if not s.valid),
"budget": len(ledger),
} |