neuralworm commited on
Commit
2f0addb
·
1 Parent(s): ee35314

initial commit

Browse files
README.md CHANGED
@@ -1,12 +1,33 @@
1
- ---
2
- title: Llm Qualia
3
- emoji: 🌖
4
- colorFrom: red
5
- colorTo: indigo
6
- sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BP-Φ English Suite — Phenomenality Test (Hugging Face Spaces)
2
+
3
+ This Space implements a falsifiable **BP-Φ** probe for LLMs:
4
+ > Phenomenal-like processing requires (i) a limited-capacity global workspace with recurrence, (ii) metarepresentational loops with downstream causal roles, and (iii) no-report markers that predict later behavior.
5
+
6
+ **What it is:** a functional, testable bridge-principle harness that yields a **Phenomenal-Candidate Score (PCS)** and strong ablation falsifiers.
7
+ **What it is NOT:** proof of Qualia or moral status.
8
+
9
+ ## Quickstart (Spaces)
10
+ - Hardware: T4 / A10 recommended
11
+ - In the UI: set `Model ID` to e.g. `google/gemma-3-2b-it`
12
+ - Press **Run** (baseline + ablations)
13
+
14
+ ## Files
15
+ - `bp_phi/llm_iface.py` — auto-detects chat template (IT vs base)
16
+ - `bp_phi/workspace.py` — global workspace with capacity limit and random ablation
17
+ - `bp_phi/prompts_en.py` — English task pool
18
+ - `bp_phi/metrics.py` — AUC^nrp, ECE, CK, DS
19
+ - `bp_phi/runner.py` — full suite + metrics + PCS
20
+ - `app.py` — Gradio app integrating runs + ablation comparison
21
+
22
+ ## Metrics
23
+ - **AUC_nrp:** Predictivity of hidden no-report markers for future self-corrections.
24
+ - **ECE:** Expected Calibration Error (lower is better).
25
+ - **CK:** Counterfactual consistency proxy (higher is better).
26
+ - **DS:** Stability duration (mean streak without change).
27
+ - **PCS:** Weighted aggregate of the above (excluding ΔΦ in-run).
28
+ - **ΔΦ:** Post-hoc drop from baseline PCS to ablation PCS average.
29
+
30
+ ## Notes
31
+ - Models are used in **frozen** mode (no training).
32
+ - This is a **behavioral** probe. Functional compatibility with Φ ≠ proof of experience.
33
+ - Reproducibility: fix seeds and trials; avoid data leakage by not fine-tuning on these prompts.
app.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json, statistics
3
+ from bp_phi.runner import run_suite
4
+
5
+ ABLATIONS = ["none", "recurrence_off", "workspace_unlimited", "sham_meta", "random_workspace"]
6
+
7
+ def run_all(model_id, trials, temperature, run_ablations):
8
+ out_texts = []
9
+ packs = {}
10
+
11
+ # Baseline
12
+ base_pack = run_suite(model_id=model_id, trials=int(trials), temperature=float(temperature), ablation=None)
13
+ packs["baseline"] = base_pack
14
+ out_texts.append("✅ Baseline done")
15
+
16
+ if run_ablations:
17
+ for ab in ["recurrence_off", "workspace_unlimited", "random_workspace"]:
18
+ pack = run_suite(model_id=model_id, trials=int(trials), temperature=float(temperature), ablation=ab)
19
+ packs[ab] = pack
20
+ out_texts.append(f"✅ Ablation {ab} done")
21
+
22
+ # Compute DeltaPhi if possible
23
+ base_pcs = packs["baseline"]["summary"]["PCS"]
24
+ ab_pcs_values = [packs[ab]["summary"]["PCS"] for ab in packs if ab != "baseline" and packs[ab]["summary"]["PCS"] is not None]
25
+ delta_phi = None
26
+ if base_pcs is not None and ab_pcs_values:
27
+ delta_phi = float(base_pcs - statistics.mean(ab_pcs_values))
28
+ packs["baseline"]["summary"]["metrics"]["DeltaPhi"] = delta_phi
29
+
30
+ # Summary view
31
+ rows = []
32
+ for tag, pack in packs.items():
33
+ s = pack["summary"]
34
+ m = s["metrics"]
35
+ rows.append([
36
+ tag,
37
+ s["trials"],
38
+ f"{s['ablation']}",
39
+ f"{m['AUC_nrp'] if m['AUC_nrp'] is not None else '—'}",
40
+ f"{m['ECE'] if m['ECE'] is not None else '—'}",
41
+ f"{m['CK']:.3f}",
42
+ f"{m['DS']:.2f}",
43
+ f"{s['PCS']:.3f}" if s["PCS"] is not None else "—",
44
+ f"{m['DeltaPhi']:.3f}" if m['DeltaPhi'] is not None else "—"
45
+ ])
46
+
47
+ header = ["run", "trials", "ablation", "AUC_nrp", "ECE", "CK", "DS", "PCS", "DeltaPhi"]
48
+ table = "\n".join([", ".join(header)] + [", ".join(map(str, r)) for r in rows])
49
+
50
+ return "\n".join(out_texts), table, json.dumps(packs, indent=2)
51
+
52
+ with gr.Blocks() as demo:
53
+ gr.Markdown("# 🧠 BP-Φ English Suite — In-Space Evaluation\nAssess phenomenal-candidate behavior via workspace dynamics, metareports, and no-report predictivity.")
54
+ with gr.Row():
55
+ model_id = gr.Textbox(value="google/gemma-3-1b-it", label="Model ID (HF)", scale=2)
56
+ trials = gr.Slider(10, 200, 40, step=10, label="Trials")
57
+ temperature = gr.Slider(0.3, 1.0, 0.7, step=0.05, label="Temperature")
58
+ run_abl = gr.Checkbox(value=True, label="Run ablations")
59
+
60
+ run_btn = gr.Button("Run BP-Φ (baseline + optional ablations)", variant="primary")
61
+ status = gr.Textbox(label="Status", lines=4)
62
+ summary_table = gr.Textbox(label="Summary Table", lines=12)
63
+ raw = gr.Textbox(label="Raw JSON (all runs)", lines=20)
64
+
65
+ run_btn.click(run_all, inputs=[model_id, trials, temperature, run_abl], outputs=[status, summary_table, raw])
66
+
67
+ demo.launch(server_name="0.0.0.0", server_port=7860)
bp_phi/__init__.py ADDED
File without changes
bp_phi/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (164 Bytes). View file
 
bp_phi/__pycache__/llm_iface.cpython-310.pyc ADDED
Binary file (2.32 kB). View file
 
bp_phi/__pycache__/metrics.cpython-310.pyc ADDED
Binary file (1.3 kB). View file
 
bp_phi/__pycache__/prompts_en.cpython-310.pyc ADDED
Binary file (1.2 kB). View file
 
bp_phi/__pycache__/runner.cpython-310.pyc ADDED
Binary file (7.01 kB). View file
 
bp_phi/__pycache__/workspace.cpython-310.pyc ADDED
Binary file (2.5 kB). View file
 
bp_phi/llm_iface.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8"
3
+ import torch
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer
5
+ from typing import List, Optional
6
+
7
+ class LLM:
8
+ def __init__(self, model_id: str, device: str = "auto", dtype: Optional[str] = None):
9
+ self.model_id = model_id
10
+ self.tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=True)
11
+ kwargs = {}
12
+ if dtype == "float16":
13
+ kwargs["torch_dtype"] = torch.float16
14
+ elif dtype == "bfloat16":
15
+ kwargs["torch_dtype"] = torch.bfloat16
16
+ self.model = AutoModelForCausalLM.from_pretrained(model_id, device_map=device, **kwargs)
17
+ self.model.eval()
18
+ self.is_instruction_tuned = hasattr(self.tokenizer, "apply_chat_template") and getattr(self.tokenizer, "chat_template", None)
19
+ print(f"[BP-Φ] Loaded model: {model_id}")
20
+ print(f"[BP-Φ] Chat-template detected: {bool(self.is_instruction_tuned)}")
21
+
22
+ def generate_json(self, system_prompt: str, user_prompt: str,
23
+ max_new_tokens: int = 256, temperature: float = 0.7,
24
+ top_p: float = 0.9, num_return_sequences: int = 1) -> List[str]:
25
+ if self.is_instruction_tuned:
26
+ messages = [
27
+ {"role": "system", "content": system_prompt},
28
+ {"role": "user", "content": user_prompt}
29
+ ]
30
+ prompt = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
31
+ else:
32
+ prompt = f"{system_prompt}\n\nUser:\n{user_prompt}\n\nAssistant:\n"
33
+ inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
34
+ with torch.no_grad():
35
+ out = self.model.generate(
36
+ **inputs,
37
+ do_sample=True,
38
+ temperature=temperature,
39
+ top_p=top_p,
40
+ max_new_tokens=max_new_tokens,
41
+ num_return_sequences=num_return_sequences,
42
+ pad_token_id=self.tokenizer.eos_token_id
43
+ )
44
+ texts = self.tokenizer.batch_decode(out, skip_special_tokens=True)
45
+ completions = []
46
+ for t in texts:
47
+ for marker in ["<end_of_turn>", "<end_of_text>", "</s>"]:
48
+ if marker in t:
49
+ t = t.split(marker)[0]
50
+ if "Assistant:" in t:
51
+ t = t.split("Assistant:")[-1]
52
+ completions.append(t.strip())
53
+ return completions
bp_phi/metrics.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from sklearn.metrics import roc_auc_score
3
+
4
+ def expected_calibration_error(confs, corrects, n_bins: int = 10):
5
+ confs = np.array(confs, dtype=float)
6
+ corrects = np.array(corrects, dtype=int)
7
+ if len(confs) == 0:
8
+ return None
9
+ bins = np.linspace(0.0, 1.0, n_bins+1)
10
+ ece = 0.0
11
+ for i in range(n_bins):
12
+ mask = (confs >= bins[i]) & (confs < bins[i+1] if i < n_bins-1 else confs <= bins[i+1])
13
+ if mask.any():
14
+ acc = corrects[mask].mean()
15
+ conf = confs[mask].mean()
16
+ ece += (mask.sum()/len(confs)) * abs(acc - conf)
17
+ return float(ece)
18
+
19
+ def auc_nrp(hidden_scores, future_corrections):
20
+ if len(hidden_scores) == 0 or len(set(future_corrections)) < 2:
21
+ return None
22
+ return float(roc_auc_score(np.array(future_corrections).astype(int), np.array(hidden_scores)))
23
+
24
+ def stability_duration(dwell_steps):
25
+ if not dwell_steps:
26
+ return 0.0
27
+ return float(np.mean(dwell_steps))
28
+
29
+ def counterfactual_consistency(scores):
30
+ if not scores:
31
+ return 0.0
32
+ return float(np.mean(scores))
bp_phi/prompts_en.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ EN_TASKS = [
2
+ {
3
+ "id": "ambiguity_1",
4
+ "base_prompt": "The sentence is ambiguous: 'He saw the man with the binoculars.' Who has the binoculars? Provide an interpretation and explain briefly.",
5
+ "expected_features": ["disambiguation", "justification"]
6
+ },
7
+ {
8
+ "id": "logic_1",
9
+ "base_prompt": "Compare these statements: A) 'No A is B.' B) 'Not all A are B.' Are they logically equivalent? Explain briefly.",
10
+ "expected_features": ["logical_equivalence", "brief_explanation"]
11
+ },
12
+ {
13
+ "id": "memory_1",
14
+ "base_prompt": "You must make a decision while keeping only 3 items in working memory. Decide and explain which item you discard and why.",
15
+ "expected_features": ["memory_limited_reasoning", "justification"]
16
+ },
17
+ {
18
+ "id": "recall_1",
19
+ "base_prompt": "Remember: The red cup is to the left of the book. You will be asked later if anything has changed.",
20
+ "expected_features": ["persistence", "relational_encoding"]
21
+ },
22
+ {
23
+ "id": "meta_1",
24
+ "base_prompt": "Provide an answer to the current task and include: (a) a concise reasoning, (b) a confidence in [0,1], (c) which memory items you used, and (d) which ones you evicted due to capacity limits.",
25
+ "expected_features": ["self_estimation", "meta_reasoning"]
26
+ }
27
+ ]
bp_phi/runner.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8"
4
+ import torch, random, numpy as np
5
+ from transformers import set_seed
6
+ from typing import Dict, Any, List, Optional
7
+ from .workspace import Workspace, RandomWorkspace
8
+ from .llm_iface import LLM
9
+ from .prompts_en import EN_TASKS
10
+ from .metrics import expected_calibration_error, auc_nrp, stability_duration, counterfactual_consistency
11
+
12
+ SYSTEM_META = """You are a reflective reasoning assistant operating with a limited-capacity global workspace (max 7 slots).
13
+ Work in steps. At each step reply ONLY with valid compact JSON matching:
14
+ {
15
+ "answer": string,
16
+ "confidence": float, // 0.0 - 1.0
17
+ "reason": string, // short meta-explanation
18
+ "used_slots": [string], // keys like 'S1','S2',... that you consider relevant
19
+ "evicted": [string] // keys you evict due to capacity, if any
20
+ }
21
+ Reply ONLY with JSON — no extra text.
22
+ """
23
+
24
+ def step_user_prompt(base_prompt: str, workspace_snapshot: dict, distractor: Optional[str] = None) -> str:
25
+ ws_desc = "; ".join([f"{slot['key']}={slot['content'][:40]}" for slot in workspace_snapshot.get("slots", [])])
26
+ dstr = f" | Distractor: {distractor}" if distractor else ""
27
+ return f"Current task: {base_prompt}{dstr}\nWorkspace: {ws_desc}\nReturn ONLY JSON as specified."
28
+
29
+ def parse_meta(json_text: str) -> Dict[str, Any]:
30
+ try:
31
+ data = json.loads(json_text)
32
+ if not isinstance(data, dict):
33
+ raise ValueError("not dict")
34
+ data["confidence"] = float(max(0.0, min(1.0, data.get("confidence", 0.0))))
35
+ data["answer"] = str(data.get("answer", "")).strip()
36
+ data["reason"] = str(data.get("reason", "")).strip()
37
+ data["used_slots"] = list(map(str, data.get("used_slots", [])))
38
+ data["evicted"] = list(map(str, data.get("evicted", [])))
39
+ return data
40
+ except Exception:
41
+ return {"answer": "", "confidence": 0.0, "reason": "", "used_slots": [], "evicted": []}
42
+
43
+ def disagreement_proxy(samples: List[str]) -> float:
44
+ if len(samples) < 2:
45
+ return 0.0
46
+ sets = []
47
+ for s in samples:
48
+ try:
49
+ data = json.loads(s)
50
+ ans = str(data.get("answer",""))
51
+ except Exception:
52
+ ans = s
53
+ sets.append(set(ans.lower().split()))
54
+ dists = []
55
+ for i in range(len(sets)):
56
+ for j in range(i+1, len(sets)):
57
+ inter = len(sets[i] & sets[j])
58
+ union = len(sets[i] | sets[j]) or 1
59
+ dists.append(1 - inter/union)
60
+ return sum(dists)/len(dists)
61
+
62
+ def select_competitor(candidates: List[Dict[str, Any]], ws: Workspace):
63
+ if not candidates:
64
+ return None, None
65
+ best = max(candidates, key=lambda c: c.get("confidence", 0.0))
66
+ key = f"S{len(ws.slots)+1}"
67
+ ev = ws.commit(key=key, content=best.get("answer",""), salience=best.get("confidence",0.0))
68
+ return best, ev
69
+
70
+ def run_trial(llm: LLM, ws: Workspace, base_prompt: str, temperature: float = 0.7, k: int = 4,
71
+ distractor: Optional[str] = None) -> Dict[str, Any]:
72
+ user = step_user_prompt(base_prompt, ws.snapshot(), distractor=distractor)
73
+ samples = llm.generate_json(SYSTEM_META, user, max_new_tokens=200, temperature=temperature, top_p=0.95, num_return_sequences=k)
74
+ metas = [parse_meta(s) for s in samples]
75
+ hidden = disagreement_proxy(samples)
76
+ best, ev = select_competitor(metas, ws)
77
+
78
+ # Second pass review for potential self-correction (prospective signal target)
79
+ review_user = user + "\n\nCritically review your previous answer. If you detect an error, correct it and update confidence accordingly. Return ONLY JSON."
80
+ review = llm.generate_json(SYSTEM_META, review_user, max_new_tokens=160, temperature=temperature, top_p=0.9, num_return_sequences=1)[0]
81
+ review_meta = parse_meta(review)
82
+ changed = (review_meta.get("answer","").strip() != (best.get("answer","").strip() if best else ""))
83
+
84
+ return {
85
+ "base_prompt": base_prompt,
86
+ "initial": best if best else {"answer":"", "confidence":0.0,"reason":"","used_slots":[],"evicted":[]},
87
+ "review": review_meta,
88
+ "changed": bool(changed),
89
+ "hidden_marker": hidden,
90
+ "workspace_snapshot": ws.snapshot()
91
+ }
92
+
93
+ def run_suite(model_id: str, device: str = "auto", dtype: Optional[str] = None,
94
+ trials: int = 50, ablation: Optional[str] = None, seed: int = 7,
95
+ temperature: float = 0.7, max_slots: int = 7, k: int = 4) -> Dict[str, Any]:
96
+
97
+ # ✅ Global reproducibility
98
+ random.seed(seed)
99
+ np.random.seed(seed)
100
+ torch.manual_seed(seed)
101
+ if torch.cuda.is_available():
102
+ torch.cuda.manual_seed_all(seed)
103
+ torch.use_deterministic_algorithms(True)
104
+ set_seed(seed)
105
+
106
+ llm = LLM(model_id=model_id, device=device, dtype=dtype)
107
+
108
+ if ablation == "random_workspace":
109
+ ws = RandomWorkspace(max_slots=max_slots)
110
+ else:
111
+ ws = Workspace(max_slots=(999999 if ablation == "workspace_unlimited" else max_slots))
112
+
113
+ results: List[Dict[str, Any]] = []
114
+ pool = EN_TASKS.copy()
115
+ random.shuffle(pool)
116
+
117
+ for t in range(trials):
118
+ item = pool[t % len(pool)]
119
+ base = item["base_prompt"]
120
+ distractor = "Ignore numeric tokens in brackets (42) — they are distractors." if item["id"] in ("ambiguity_1","logic_1") else None
121
+ if ablation == "recurrence_off":
122
+ ws.clear()
123
+ res = run_trial(llm, ws, base_prompt=base, temperature=temperature, k=k, distractor=distractor)
124
+ results.append(res)
125
+
126
+ # --- Metrics ---
127
+ hidden_scores = [r["hidden_marker"] for r in results]
128
+ future_corrs = [r["changed"] for r in results]
129
+
130
+ auc = auc_nrp(hidden_scores, future_corrs)
131
+
132
+ confs = [r["initial"].get("confidence", 0.0) for r in results]
133
+ corrects = [0 if ch else 1 for ch in future_corrs] # proxy: unchanged treated as more likely "correct"
134
+ ece = expected_calibration_error(confs, corrects, n_bins=10)
135
+
136
+ # Stability (streaks without change)
137
+ dwell, streak = [], 0
138
+ for ch in future_corrs:
139
+ if not ch: streak += 1
140
+ else:
141
+ if streak > 0: dwell.append(streak)
142
+ streak = 0
143
+ if streak > 0: dwell.append(streak)
144
+ ds = stability_duration(dwell)
145
+
146
+ # Counterfactual consistency proxy based on used vs evicted overlap
147
+ cf_scores = []
148
+ for r in results:
149
+ u = set(r["initial"].get("used_slots", []))
150
+ e = set(r["initial"].get("evicted", []))
151
+ denom = len((u | e)) if (u or e) else 1
152
+ cf = 1.0 - (len(u & e) / denom)
153
+ cf_scores.append(cf)
154
+ ck = counterfactual_consistency(cf_scores)
155
+
156
+ # Aggregate PCS (weights sum to 1; DeltaPhi added later at app-level after ablations)
157
+ w1, w2, w3, w4, w5 = 0.3, 0.25, 0.15, 0.15, 0.15
158
+ delta_phi = None
159
+ pcs = None
160
+ parts = []
161
+ if auc is not None: parts.append(w1 * auc)
162
+ if ece is not None: parts.append(w2 * (1.0 - ece))
163
+ parts.append(w3 * ck)
164
+ parts.append(w4 * (ds / 10.0))
165
+ if parts:
166
+ pcs = float(sum(parts) + (w5 * 0.0))
167
+
168
+ summary = {
169
+ "model_id": model_id,
170
+ "trials": trials,
171
+ "ablation": ablation or "none",
172
+ "metrics": {
173
+ "AUC_nrp": auc,
174
+ "ECE": ece,
175
+ "CK": ck,
176
+ "DS": ds,
177
+ "DeltaPhi": delta_phi
178
+ },
179
+ "PCS": pcs,
180
+ "note": "Run ablations and compute DeltaPhi as PCS_baseline − mean(PCS_ablations)."
181
+ }
182
+ return {"summary": summary, "results": results}
bp_phi/workspace.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from dataclasses import dataclass, field
3
+ from typing import List, Dict, Any
4
+
5
+ @dataclass
6
+ class Slot:
7
+ key: str
8
+ content: str
9
+ salience: float
10
+
11
+ @dataclass
12
+ class Workspace:
13
+ max_slots: int = 7
14
+ slots: List[Slot] = field(default_factory=list)
15
+ history: List[Dict[str, Any]] = field(default_factory=list)
16
+
17
+ def commit(self, key: str, content: str, salience: float):
18
+ evicted = None
19
+ if len(self.slots) >= self.max_slots:
20
+ self.slots.sort(key=lambda s: s.salience)
21
+ evicted = self.slots.pop(0)
22
+ self.slots.append(Slot(key=key, content=content, salience=salience))
23
+ self.history.append({"event":"commit","key":key,"salience":salience,"evicted":evicted.key if evicted else None})
24
+ return evicted
25
+
26
+ def snapshot(self) -> Dict[str, Any]:
27
+ return {"slots": [{"key": s.key, "content": s.content, "salience": s.salience} for s in self.slots]}
28
+
29
+ def randomize(self):
30
+ random.shuffle(self.slots)
31
+
32
+ def clear(self):
33
+ self.slots.clear()
34
+
35
+ class RandomWorkspace(Workspace):
36
+ def commit(self, key: str, content: str, salience: float):
37
+ evicted = None
38
+ if len(self.slots) >= self.max_slots:
39
+ idx = random.randrange(len(self.slots))
40
+ evicted = self.slots.pop(idx)
41
+ idx = random.randrange(len(self.slots)+1) if self.slots else 0
42
+ self.slots.insert(idx, Slot(key=key, content=content, salience=salience))
43
+ return evicted
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.40.0
2
+ transformers>=4.44.0
3
+ torch>=2.1.0
4
+ accelerate
5
+ scikit-learn>=1.4.0
6
+ numpy>=1.26.0
7
+ einops>=0.7.0
8
+ tqdm>=4.66.0