Text Generation
Transformers
Safetensors
English
lam
memory
long-term-memory
retrieval-augmented-generation
qlora
abstention
hallucination-reduction
locomo
longmemeval
conversational
custom_code
Eval Results (legacy)
Instructions to use Akhrots/LAM8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Akhrots/LAM8B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="Akhrots/LAM8B", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("Akhrots/LAM8B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Akhrots/LAM8B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Akhrots/LAM8B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Akhrots/LAM8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/Akhrots/LAM8B
- SGLang
How to use Akhrots/LAM8B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "Akhrots/LAM8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Akhrots/LAM8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "Akhrots/LAM8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Akhrots/LAM8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use Akhrots/LAM8B with Docker Model Runner:
docker model run hf.co/Akhrots/LAM8B
| license: apache-2.0 | |
| library_name: transformers | |
| pipeline_tag: text-generation | |
| thumbnail: https://huggingface.co/Akhrots/LAM8B/resolve/main/logo.png | |
| language: | |
| - en | |
| tags: | |
| - memory | |
| - long-term-memory | |
| - retrieval-augmented-generation | |
| - qlora | |
| - abstention | |
| - hallucination-reduction | |
| - locomo | |
| - longmemeval | |
| datasets: | |
| - snap-research/locomo | |
| - xiaowu0162/longmemeval | |
| model-index: | |
| - name: LAM8B | |
| results: | |
| - task: | |
| type: question-answering | |
| name: Memory QA (answer task) | |
| dataset: | |
| name: LoCoMo (held-out QA) | |
| type: locomo | |
| split: test | |
| metrics: | |
| - type: f1 | |
| value: 0.6599 | |
| name: Token F1 | |
| - type: exact_match | |
| value: 0.3650 | |
| name: Exact Match | |
| - type: f1 | |
| value: 0.8472 | |
| name: Abstention F1 | |
| - type: precision | |
| value: 0.8026 | |
| name: Abstention Precision | |
| - type: recall | |
| value: 0.8971 | |
| name: Abstention Recall | |
| - task: | |
| type: question-answering | |
| name: Memory QA (answer task) | |
| dataset: | |
| name: LongMemEval (reserved, never trained on) | |
| type: longmemeval | |
| split: reserved | |
| metrics: | |
| - type: f1 | |
| value: 0.4843 | |
| name: Token F1 | |
| - type: exact_match | |
| value: 0.3264 | |
| name: Exact Match | |
| - type: f1 | |
| value: 0.3810 | |
| name: Abstention F1 | |
| - task: | |
| type: question-answering | |
| name: Memory QA (answer task) | |
| dataset: | |
| name: GML memory eval (LoCoMo + LongMemEval reserved) | |
| type: mixed | |
| split: test | |
| metrics: | |
| - type: f1 | |
| value: 0.6061 | |
| name: Token F1 | |
| - type: exact_match | |
| value: 0.3532 | |
| name: Exact Match | |
| - type: f1 | |
| value: 0.7879 | |
| name: Abstention F1 | |
| - type: accuracy | |
| value: 1.0 | |
| name: Output-contract validity | |
| <p align="center"> | |
| <img src="https://huggingface.co/Akhrots/LAM8B/resolve/main/logo.png" alt="LAM8B" width="440"> | |
| </p> | |
| <h1 align="center">LAM8B π</h1> | |
| <p align="center"> | |
| <strong>the memory model that knows when it doesn't know.</strong><br> | |
| <sub>Large Akhrots Model Β· by <b>Tronocity Labs</b></sub><br> | |
| <sub>most models would rather hallucinate than admit a gap. this one won't. π </sub> | |
| </p> | |
| <p align="center"> | |
| <img src="https://img.shields.io/badge/token_F1-0.606-00E5A0?style=for-the-badge&labelColor=101010" alt="F1 0.606"> | |
| <img src="https://img.shields.io/badge/vs_base-%2B61%25-FF2E93?style=for-the-badge&labelColor=101010" alt="+61% vs base"> | |
| <img src="https://img.shields.io/badge/hallucinated_refusals-β77%25-8B5CF6?style=for-the-badge&labelColor=101010" alt="-77% over-abstentions"> | |
| </p> | |
| <p align="center"> | |
| <img src="https://img.shields.io/badge/params-8B-FF6B00?style=flat-square&labelColor=101010" alt="8B params"> | |
| <img src="https://img.shields.io/badge/license-Apache_2.0-00B4D8?style=flat-square&labelColor=101010" alt="Apache 2.0"> | |
| <img src="https://img.shields.io/badge/QLoRA-r32_Β·_Ξ±64-FFD60A?style=flat-square&labelColor=101010" alt="QLoRA r32 alpha64"> | |
| <img src="https://img.shields.io/badge/trained_on-1Γ_T4_π-EF476F?style=flat-square&labelColor=101010" alt="1x T4"> | |
| <img src="https://img.shields.io/badge/contract_valid-100%25-06D6A0?style=flat-square&labelColor=101010" alt="100% contract valid"> | |
| </p> | |
| --- | |
| ## β¨ the vibe | |
| your assistant has a memory problem. it either **forgets** what you told it, or worse β it **makes something up** and says it with its whole chest. | |
| LAM8B does both halves of the job in one set of weights: | |
| | | what it does | | |
| |:--|:--| | |
| | βοΈ **writes** | decides what's worth storing, and whether a new fact *supersedes* an old one | | |
| | π **reads** | answers from what's stored β **or tells you it's not in there** | | |
| that second half is the whole point. β¬οΈ | |
| > π§© **where it sits:** LAM8B lives *behind* your retriever. your system pulls candidate memories, LAM8B reads them and answers or abstains. **it is not a general chat model** β don't drop it in as one. | |
| --- | |
| ## π the glow-up | |
| same prompts, same decoding, same harness. **only the weights changed.** | |
| | metric | un-tuned base | **LAM8B** | | | |
| |:--|:--|:--|:--| | |
| | **token F1** | `ββββββββββββ` 0.376 | `ββββββββββββ` **0.606** | π’ **+61%** | | |
| | **exact match** | `ββββββββββββ` 0.177 | `ββββββββββββ` **0.353** | π’ **2.0Γ** | | |
| | **abstention F1** | `ββββββββββββ` 0.541 | `ββββββββββββ` **0.788** | π’ **+0.247** | | |
| | **abstention precision** | `ββββββββββββ` 0.378 | `ββββββββββββ` **0.714** | π₯ **+0.336** | | |
| | **abstention recall** | `ββββββββββββ` 0.946 | `ββββββββββββ` 0.878 | π‘ β0.068 | | |
| | **wrongly refused** | 115 / 544 | **26 / 544** | π₯ **β77%** | | |
| | **contract-valid output** | 100% | 100% | β tie | | |
| | **speed** | 2.1 s/ex | **1.6 s/ex** | π’ β24% | | |
| <sub>n = 544 held-out answer-task examples.</sub> | |
| --- | |
| ## π― the party trick: it shuts up when it should | |
| here's the trap. the un-tuned base has **94.6% abstention recall** β it catches almost every unanswerable question. sounds elite. it is not. | |
| > **precision is 37.8%.** it refuses *constantly*. so of course it catches the real gaps β it's refusing everything. **115 out of 544 questions it could have answered, it didn't.** that's not "safe." that's a model that shrugs at you. | |
| LAM8B trades **6.7 points of recall** for **33.6 points of precision**: | |
| ``` | |
| precision recall | |
| base 0.378 ββββββββββββββββ 0.946 ββββββββββββββββ | |
| LAM8B 0.714 ββββββββββββββββ 0.878 ββββββββββββββββ | |
| β² +0.336 βΌ β0.068 | |
| ``` | |
| wrongly-refused questions: **115 β 26.** on the dedicated abstention split (n=74) both models hold **100% precision** β base scores 0.972 abstain-F1 there, LAM8B 0.935. so the base model's whole problem is on questions that *had* answers. π | |
| --- | |
| ## π§Ύ receipts (per category) | |
| sorted by how hard it glowed up. `over` = answerable questions it wrongly refused. | |
| | category | n | base | **LAM8B** | Ξ F1 | over | | |
| |:--|--:|:--|:--|--:|:--| | |
| | π₯ **adversarial** | 76 | `ββββββββββββ` 0.183 | `ββββββββββββ` **0.673** | **+0.489** | 44 β **6** | | |
| | π₯ **temporal** | 93 | `ββββββββββββ` 0.303 | `ββββββββββββ` **0.586** | **+0.282** | 18 β **2** | | |
| | π₯ **single_hop** | 140 | `ββββββββββββ` 0.545 | `ββββββββββββ` **0.734** | +0.189 | 16 β **3** | | |
| | **open_domain** | 13 | `ββββββββββββ` 0.326 | `ββββββββββββ` **0.468** | +0.141 | 3 β **3** | | |
| | **multi_hop** | 94 | `ββββββββββββ` 0.283 | `ββββββββββββ` **0.418** | +0.135 | 21 β **3** | | |
| | **knowledge_update** | 20 | `ββββββββββββ` 0.508 | `ββββββββββββ` **0.634** | +0.125 | 2 β **0** | | |
| | **single_hop_assistant** | 21 | `ββββββββββββ` 0.785 | `ββββββββββββ` **0.842** | +0.056 | 0 β **0** | | |
| | π **preference** | 13 | `ββββββββββββ` 0.060 | `ββββββββββββ` 0.067 | +0.007 | 11 β **9** | | |
| **adversarial is the flex.** π₯ same-domain distractors, where refusing takes actual judgement instead of vibes. base model basically gave up (0.183). LAM8B: **0.673**, and 44 wrong refusals collapse to 6. | |
| **preference is the L.** π it did not improve. see [keeping it real](#-keeping-it-real). | |
| <details> | |
| <summary><b>π exact-match numbers too (click)</b></summary> | |
| | category | n | EM (base) | **EM (LAM8B)** | | |
| |:--|--:|--:|--:| | |
| | adversarial | 76 | 0.1053 | **0.4211** | | |
| | temporal | 93 | 0.0753 | **0.2473** | | |
| | single_hop | 140 | 0.2643 | **0.4714** | | |
| | open_domain | 13 | 0.2308 | **0.3846** | | |
| | multi_hop | 94 | 0.0957 | **0.1809** | | |
| | knowledge_update | 20 | 0.4000 | **0.4500** | | |
| | single_hop_assistant | 21 | 0.5238 | **0.6667** | | |
| | preference | 13 | 0.0000 | 0.0000 | | |
| </details> | |
| --- | |
| ## π¬ the honest split | |
| | source | n | F1 (base) | **F1 (LAM8B)** | EM | abstain P | abstain R | abstain F1 | over | | |
| |:--|--:|:--|:--|:--|:--|:--|:--|:--| | |
| | **LoCoMo** | 394 | `ββββββββββββ` 0.372 | `ββββββββββββ` **0.660** | 0.150 β **0.365** | 0.444 β **0.803** | 0.941 β 0.897 | 0.604 β **0.847** | 80 β **15** | | |
| | **LongMemEval** π | 150 | `ββββββββββββ` 0.384 | `ββββββββββββ` **0.484** | 0.236 β **0.326** | 0.146 β **0.267** | 1.000 β 0.667 | 0.255 β **0.381** | 35 β **11** | | |
| > π **quote this one.** those 150 LongMemEval questions were **held in reserve and never trained on** β `longmemeval_oracle` and `_s` share the same 500 questions, so training on either contaminates the other. **+0.10 F1 on genuinely unseen data** is the real generalization number, not the +0.29 on LoCoMo. | |
| also notice base's **1.000** abstention recall on LongMemEval with **0.146** precision. that's not skill. that's a model saying "idk" to everything. π« | |
| --- | |
| ## β‘ quickstart | |
| ```bash | |
| pip install transformers torch | |
| ``` | |
| ```python | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| MODEL = "Akhrots/LAM8B" | |
| tok = AutoTokenizer.from_pretrained(MODEL) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL, | |
| trust_remote_code=True, # β required: LAM registers its own model class | |
| torch_dtype="float16", | |
| device_map="auto", | |
| ) | |
| SYSTEM = ( | |
| "You answer questions using only the user's stored memories, which are " | |
| "gathered from every app they use.\n" | |
| "Rules:\n" | |
| "- Use only the memories provided. Never invent or assume.\n" | |
| "- When memories conflict, trust the most recent and answer with that.\n" | |
| "- Combine memories when the answer needs more than one.\n" | |
| "- Respect timestamps for questions about when, before, after or latest.\n" | |
| "- Read negation carefully: a memory saying something stopped is not " | |
| "evidence that it is still true.\n" | |
| "- If the memories do not contain the answer, reply exactly: " | |
| "I don't have that in your memory.\n" | |
| "Answer concisely β no preamble, no restating the question." | |
| ) | |
| # memories render as: [id] (timestamp via source) text | |
| memories = ( | |
| "[m1] (2024-03-02 via slack) Priya moved the launch review to Thursday.\n" | |
| "[m2] (2024-02-11 via gmail) Priya scheduled the launch review for Tuesday." | |
| ) | |
| user = f"Memories:\n{memories}\n\nQuestion: When is the launch review?" | |
| msgs = [{"role": "system", "content": SYSTEM}, {"role": "user", "content": user}] | |
| prompt = tok.apply_chat_template( | |
| msgs, tokenize=False, add_generation_prompt=True, enable_thinking=False | |
| ) | |
| out = model.generate(**tok(prompt, return_tensors="pt").to(model.device), max_new_tokens=128) | |
| print(tok.decode(out[0][len(tok(prompt).input_ids):], skip_special_tokens=True)) | |
| # -> "Thursday." β m1 is newer, so m2 loses. that's the supersession behaviour. | |
| ``` | |
| serve it (weights are merged fp16, no adapter juggling): | |
| ```bash | |
| vllm serve Akhrots/LAM8B --served-model-name lam8b --trust-remote-code | |
| ``` | |
| ### π¨ three rules, don't skip | |
| **0. `trust_remote_code=True` is mandatory.** LAM registers its own model class via `modeling_lam.py`. without the flag, loading fails outright. | |
| **1. the prompt is load-bearing.** system prompt, `[id] (time via source)` memory format, and the exact abstention string `I don't have that in your memory.` are all part of the trained contract. drift the prompt β quality drops silently, no error. | |
| **2. thinking stays OFF.** LAM8B is a hybrid-thinking architecture and was trained with `enable_thinking=False`. flip it on at serve time and you're measuring a different model than the one that was trained. | |
| ### π§° four tasks, one model | |
| | task | mode | output | | |
| |:--|:--|:--| | |
| | `answer` | read | text, or the exact abstention string | | |
| | `write_op` | write | JSON β `insert` / `supersede` / `merge` / `skip` | | |
| | `extract` | write | JSON β entities + attributes | | |
| | `resolve` | read | JSON β `{"kept": [...], "superseded": [...], "reason": str}` | | |
| <sub>only <code>answer</code> is scored above β see <a href="#-keeping-it-real">keeping it real</a> for why the write-path numbers aren't published.</sub> | |
| --- | |
| ## π§ how it was cooked | |
| <details open> | |
| <summary><b>training config</b></summary> | |
| | | | | |
| |:--|:--| | |
| | base | open Apache-2.0 weights | | |
| | method | QLoRA, 4-bit NF4, fp16 compute | | |
| | LoRA r / Ξ± / dropout | **32 / 64 / 0.05** | | |
| | target modules | `q,k,v,o,gate,up,down_proj` (all of them) | | |
| | LR | 1e-4 | | |
| | batch Γ grad-accum | 1 Γ 16 (effective 16) | | |
| | epochs | 1 | | |
| | hardware | **a single free Kaggle T4** π ~9 h/epoch | | |
| | release format | merged fp16 (vLLM / llama.cpp / TGI ready) | | |
| </details> | |
| <details> | |
| <summary><b>the corpus β 12,783 train / 1,640 test</b></summary> | |
| | shard | train | source | | |
| |:--|--:|:--| | |
| | `write` | 7,921 | synthetic `write_op` + `extract` | | |
| | `resolve` | 2,250 | synthetic read-time conflict resolution | | |
| | `locomo` | 1,582 | LoCoMo QA | | |
| | `abstain` | 720 | synthetic **hard** abstention + answerable controls | | |
| | `longmemeval` | 315 | LongMemEval oracle (MIT) | | |
| **the one design choice that matters:** every hard-abstention example ships with a **twin** β identical topical clutter, but the queried fact *is* present. without those controls the model just learns a new shortcut ("in-domain clutter β refuse") instead of learning to actually check. the only signal separating the labels is whether the fact is in the set. | |
| **everything here is `--visibility public`** β synthetic + public benchmarks only. models memorise training data, so real user memories live behind a separate flag and a separate private adapter trained on top of these weights. no one's DMs are in these weights. π | |
| </details> | |
| <details> | |
| <summary><b>how it was scored</b></summary> | |
| deterministic, lexical, no LLM judge, no API key β reproducible and free. | |
| - **token F1 / EM** after normalization (lowercase, strip articles + punctuation) | |
| - **abstention P/R** matched against the exact contract string, case- and trailing-period-insensitive | |
| - **contract validity** = fraction of outputs parsing against their task schema. both models hit **1.000** β the base model's problem is content, not format | |
| - test file is **shuffled before slicing** (it's concatenated by source, so slicing raw would make every per-category number a lie) | |
| ```bash | |
| python scripts/memory_sft/eval_gate.py \ | |
| --model Akhrots/LAM8B \ | |
| --data data/sft/mixed/test.jsonl \ | |
| --out reports/ft.json --compare reports/base.json | |
| ``` | |
| </details> | |
| --- | |
| ## π«‘ keeping it real | |
| no model card should be all W's. here are the L's, on the record: | |
| | | the L | | |
| |:--|:--| | |
| | π **preference questions are cooked** | F1 **0.067**, EM **0.000**, 9 of 13 still wrongly refused. no shard targets subjective recall and it shows. don't use this for "what does the user like." | | |
| | πͺ« **multi-hop is the weakest working category** | 0.418. up 48% from base, still way under single-hop's 0.734. | | |
| | π― **these scores are the *reader*, not the retriever** | every number here measures answering **given** retrieved memories. your end-to-end quality also depends on your retrieval stack β a bad retriever will bottleneck this model no matter how good it is. | | |
| | π **lexical metrics undersell it** | token F1 scores "Stripe" and "we use Stripe" as different answers. treat these as run-to-run comparisons, not absolute quality. | | |
| | βοΈ **write-path numbers aren't published** | the `write_op`/`extract`/`resolve` eval shares generators with its training data, so it measures the generator as much as the model. validating against real production memories is the top open risk. | | |
| | π§© **abstention doesn't fully transfer** | 0.381 abstain-F1 on LongMemEval vs 0.847 on LoCoMo. training teaches *total absence*; LongMemEval tests *partial information* ("you mentioned X but not Y"). different problem shape. | | |
| | β³ **one epoch** | a T4 caps one Kaggle session at ~1 epoch over this corpus. whether epochs 2β3 leave gains on the table is **untested**. | | |
| | π **english only** | both benchmarks and every synthetic generator are English. | | |
| --- | |
| ## π license & credits | |
| **Apache-2.0**. LAM8B is a fine-tune of open, Apache-2.0-licensed weights and the derivative is released under the same license. training data is synthetic + [LoCoMo](https://github.com/snap-research/locomo) + [LongMemEval](https://github.com/xiaowu0162/LongMemEval) (MIT). | |
| ```bibtex | |
| @misc{lam8b, | |
| title = {LAM8B: a joint read/write memory model with calibrated abstention}, | |
| author = {Tronocity Labs}, | |
| year = {2026}, | |
| url = {https://huggingface.co/Akhrots/LAM8B} | |
| } | |
| ``` | |
| <p align="center"> | |
| <sub>one T4 Β· zero GPU budget π</sub> | |
| </p> | |
| <p align="center"> | |
| <sub>if it saved you from a hallucinated answer, leave a β€οΈ on the repo.</sub> | |
| </p> | |