prehend: a language-model harness that learns - recursive context offload with self-reflective program search and experience memory.
Project description
prehend
A language-model harness that learns: recursive context offload, self-reflective program search, and experience memory.
prehend (to grasp - both comprehend and seize) is a harness that learns from the long-context problems it solves. It builds on rlms, the MIT OASYS lab's inference engine for Recursive Language Models (RLMs) - which replaces the canonical llm.completion(prompt) call with rlm.completion(prompt): the context is offloaded into a variable inside a REPL environment, and the model writes programs that slice, search, and recursively query that context instead of attending over it directly. prehend keeps that engine and adds the missing axis - a memory of what worked - so each solve makes the next one cheaper and better.
It layers three things on top of the upstream engine:
- Experience memory. Completed solves are distilled into reusable bank entries, embedded, and retrieved on later tasks, so the harness carries forward strategies that worked instead of re-deriving them every run. This is the capability the prehend name is about (see
docs/decisions/0005-prehend-experience-memory-layer.md). - Map-reduce style orchestration. Patches that harden the orchestrator-plus-workers pattern: long contexts are chunked and fanned out to parallel batched sub-calls (the map), and the orchestrator aggregates the partial answers (the reduce). Adds distinct system prompts for the orchestrator and its workers, per-child iteration budgets, and client fixes needed to drive local OpenAI-compatible servers reliably.
- Self-reflective program search (SRLM). An
SRLMsubclass implementing uncertainty-guided trajectory selection per Apple's SRLM paper: generate K candidate context-interaction trajectories, then select using the model's own uncertainty signals (self-consistency, verbalized confidence, reasoning trace length) instead of trusting a single rollout. The same paper motivates context-length routing, since recursive decomposition often hurts when the context already fits the model's window.
Lineage
| Stage | What it contributed |
|---|---|
rlms 0.1.1 (Zhang, Kraska, Khattab) |
The RLM paradigm and engine: REPL environments, recursive sub-calls, parallel rlm_query_batched, clients, logging, visualizer |
Local rlms patches |
Map-reduce orchestration support: child_system_prompt (workers get a different system prompt than the orchestrator), child_max_iterations, max_output_chars stdout truncation, default_extra_body on the OpenAI client, consecutive same-role message merging (required by llama-server), response_format pass-through |
prehend |
The SRLM subclass (context-length routing, multi-trajectory generation with parallel candidates, joint uncertainty-guided selection) plus the experience-memory layer that distills and retrieves past solves |
SRLM: uncertainty-guided trajectory selection
The quality of an RLM answer depends heavily on which program trajectory the model happens to sample. SRLM subclasses RLM and replaces single-rollout inference with search over K candidates:
from prehend import SRLM
srlm = SRLM(
backend="openai",
backend_kwargs={"model_name": "my-model", "base_url": "http://localhost:8080/v1"},
direct_threshold=30_000, # contexts under 30K chars skip the REPL entirely
n_candidates=4, # K candidate trajectories
candidate_parallel=2, # candidates in flight at once (match server slots)
candidate_temperature=0.7, # sampling diversity across candidates
confidence_elicitation=True, # elicit per-step {"confidence": N} and use it in selection
)
result = srlm.completion(long_context, "What changed between Q3 and Q4?")
How a winner is chosen, per the SRLM paper:
- Self-consistency. Final answers are clustered semantically (normalization plus word-boundary containment, so "42" and "The answer is 42" vote together) and the plurality cluster survives. Tied clusters pool their candidates rather than favoring whichever answer appeared first.
- Joint uncertainty score. Within the surviving set, each trajectory gets
VC(p) * Len(p), whereVCis the sum of log per-step verbalized confidences (steps that skip reporting are imputed with the trajectory mean, so under-reporting cannot inflate the score) andLenis the trace length in output tokens. The candidate closest to zero wins. Withoutconfidence_elicitation, selection falls back to the shortest trace.
Implementation notes:
- Each candidate runs on a fresh
RLMinstance with its own logger and config copy, so parallel candidates share no mutable state. A crashing candidate is dropped; only if every candidate fails does the call raise. confidence_elicitation=Trueappends the reporting instruction to the system prompt automatically; spawned candidates inherit it.direct_thresholdroutes short contexts to a plain LLM call. The SRLM paper finds recursive decomposition frequently underperforms the base model within its native window, so set this to roughly the served context size.
| Parameter | Default | Meaning |
|---|---|---|
direct_threshold |
0 (off) |
Context length in chars below which the REPL is bypassed |
n_candidates |
1 |
Candidate trajectories per completion |
candidate_parallel |
1 |
Candidates run concurrently (thread pool) |
candidate_temperature |
None |
Temperature injected into candidate backends |
confidence_elicitation |
False |
Elicit per-step confidence and use VC*Len selection |
All RLM constructor arguments pass through unchanged, including child_system_prompt.
Install
Requires Python 3.11+. Available on PyPI; note that pip install rlms installs the upstream package, not this fork.
pip install prehend
For development, install editable from a checkout:
uv pip install -e /path/to/prehend --no-deps
Verify you got the fork and not a stale upstream build:
python -c "import inspect; from prehend import RLM, SRLM; print('child_system_prompt' in inspect.signature(RLM.__init__).parameters)"
Quick start
from prehend import RLM
rlm = RLM(
backend="openai",
backend_kwargs={"model_name": "gpt-5-nano"},
verbose=True,
)
print(rlm.completion("Print me the first 100 powers of two, each on a newline.").response)
For the orchestrator/worker split used in map-reduce style runs:
rlm = RLM(
backend="openai",
backend_kwargs={...},
custom_system_prompt=ORCHESTRATOR_PROMPT, # the root model plans and reduces
child_system_prompt=WORKER_PROMPT, # sub-call workers map over chunks
child_max_iterations=5,
max_concurrent_subcalls=4,
)
REPL environments
Non-isolated environments run code on the host (fine for benchmarking, not for untrusted prompts); isolated environments run in cloud sandboxes. Natively supported: local (default), ipython, docker, modal, prime, daytona, e2b.
rlm = RLM(
environment="local",
environment_kwargs={"max_output_chars": 500},
)
local: in-processexecwith namespaced globals.max_output_charstruncates REPL stdout fed back to the model.ipython(pip install 'prehend[ipython]'): real IPython session, in-process or in anipykernelsubprocess with hard cell timeouts.docker: REPL inside a container (python:3.11-slimby default).modal/prime/daytona/e2b: fully isolated cloud sandboxes; sub-calls are proxied back to the host.
Model providers
OpenAI, Anthropic, OpenRouter, and Portkey clients are included. Local models work through any OpenAI-compatible server (vLLM, llama-server); the fork's default_extra_body and same-role message merging exist specifically to make local serving smooth. See prehend/clients/ to add providers.
Trajectory metadata and logging
RLMChatCompletion.metadata holds the full trajectory (run config plus every iteration and sub-call) when a logger is attached. SRLM relies on this for confidence scoring, and spawns per-candidate loggers automatically.
from prehend import RLM
from prehend.logger import RLMLogger
logger = RLMLogger(log_dir="./logs") # omit log_dir for in-memory only
rlm = RLM(..., logger=logger)
JSONL logs feed the bundled visualizer:
cd visualizer/
npm run dev # default localhost:3001
Citations
This fork builds directly on two papers. The engine:
@misc{zhang2026recursivelanguagemodels,
title={Recursive Language Models},
author={Alex L. Zhang and Tim Kraska and Omar Khattab},
year={2026},
eprint={2512.24601},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2512.24601},
}
The selection strategy:
@misc{alizadeh2026srlm,
title={Recursive Language Models Meet Uncertainty: The Surprising Effectiveness of Self-Reflective Program Search for Long Context},
author={Keivan Alizadeh and Parshin Shojaee and Minsik Cho and Mehrdad Farajtabar},
year={2026},
eprint={2603.15653},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2603.15653},
}
Upstream documentation, blogpost, and minimal implementation: docs | blogpost | rlm-minimal.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file prehend-0.2.0.tar.gz.
File metadata
- Download URL: prehend-0.2.0.tar.gz
- Upload date:
- Size: 193.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0f5fbace6dc4470bf7cba08f1fb77c62c76d843c30e0933b4fddc2a24c74543
|
|
| MD5 |
8bd06279554aa85640da86e39b15b749
|
|
| BLAKE2b-256 |
7f9860b5642699d3e70c8f440fb607ebf4a45b31bdadde130c7b8d9cfc6d378e
|
Provenance
The following attestation bundles were made for prehend-0.2.0.tar.gz:
Publisher:
publish.yml on potto007/prehend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
prehend-0.2.0.tar.gz -
Subject digest:
f0f5fbace6dc4470bf7cba08f1fb77c62c76d843c30e0933b4fddc2a24c74543 - Sigstore transparency entry: 1893812885
- Sigstore integration time:
-
Permalink:
potto007/prehend@d94578dc99e7e18b3b8f0d32b9e5bba03951fa3b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/potto007
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d94578dc99e7e18b3b8f0d32b9e5bba03951fa3b -
Trigger Event:
release
-
Statement type:
File details
Details for the file prehend-0.2.0-py3-none-any.whl.
File metadata
- Download URL: prehend-0.2.0-py3-none-any.whl
- Upload date:
- Size: 155.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38dd5f4ae0f299411b0bc8a2dddd8e23d98e704948fd18bb398f6f58c5a73512
|
|
| MD5 |
96f2f941ebdda329fd4b9a38efc4e765
|
|
| BLAKE2b-256 |
e70b8668a1c56ec87c6a2e3299cc7231a30f8d3428e494d09781d19c91e58e1a
|
Provenance
The following attestation bundles were made for prehend-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on potto007/prehend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
prehend-0.2.0-py3-none-any.whl -
Subject digest:
38dd5f4ae0f299411b0bc8a2dddd8e23d98e704948fd18bb398f6f58c5a73512 - Sigstore transparency entry: 1893813044
- Sigstore integration time:
-
Permalink:
potto007/prehend@d94578dc99e7e18b3b8f0d32b9e5bba03951fa3b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/potto007
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d94578dc99e7e18b3b8f0d32b9e5bba03951fa3b -
Trigger Event:
release
-
Statement type: