Skip to content

04 · Context management & serialization

Context-window policies

Long conversations overflow the model's context. A ContextPolicy decides what to keep. Crucially it runs on the typed segments (system / moderator / private_context / turns) before the family flatten, so framing is preserved reliably.

from interlens import (
    Conversation, ErrorPolicy, DropOldestPolicy, SlidingWindowPolicy, SummarizePolicy,
)

# ErrorPolicy (default): raise if the view exceeds context_limit — never silently truncate.
conv = Conversation(participants=(alice, bob), context_policy=ErrorPolicy(), context_limit=8192)

# SlidingWindowPolicy: keep framing + the most recent `keep_last` turns; drop older ones.
conv = Conversation(participants=(alice, bob), context_policy=SlidingWindowPolicy(keep_last=8))

# DropOldestPolicy: drop oldest turns until it fits (needs context_limit).
conv = Conversation(participants=(alice, bob), context_policy=DropOldestPolicy(), context_limit=8192)

# SummarizePolicy: replace older middle turns with a summary; keep framing + last `keep_last` verbatim.
conv = Conversation(
    participants=(alice, bob),
    context_policy=SummarizePolicy(keep_last=4, summarizer=lambda turns: "Earlier: " + " | ".join(turns)),
)

context_limit=None (default) means the tokenizer's own model_max_length is used.

Serialization: recipe + transcript

There is no separate template type: a Conversation is its own recipe. Because its participants are lazy (they hold an HF id + settings, not weights), an unrun conversation is a cheap, serializable spec — build it, ship it to workers, or run it many times, all as one object.

from interlens import Conversation, AutoModelParticipant, SlidingWindowPolicy

conv = Conversation(
    participants=[
        AutoModelParticipant.from_pretrained("Qwen/Qwen2.5-3B-Instruct", name="alice", temperature=0.7, system_prompt="Be terse."),
        AutoModelParticipant.from_pretrained("google/gemma-2-2b-it", name="bob", temperature=0.9),
    ],
    shared_context="Debate: is a hotdog a sandwich?",
    shared_system_prompt="Stay civil.",
    context_policy=SlidingWindowPolicy(keep_last=8),
    reasoning_visibility="strip",
).turns(6)                                   # rollout/data fields via dot-modifier sugar

conv.run(turns=6)                            # loads the models lazily on first use
conv.save("runs/hotdog")                     # writes conversation.json (recipe) + transcript.json
later = Conversation.load("runs/hotdog")     # rebuilds lazy participants + attaches the transcript

save records each participant's own constructor kwargs (HF id + dtype/attn/quant/revision/max_new_tokens/temperature/top_p/seed/thinking/tool names/max_tool_iters/kv_reuse, or an API provider + model id) plus the scenario framing/policies — never weights. load raises on an unsupported (older) schema rather than silently mis-reading it.

Copy-on-write updates: conv.set(field=value) (or a dot-modifier like conv.turns(6)) returns a modified copy sharing the loaded models by reference — the original is untouched.

Save / load a whole conversation (recipe + transcript)

conv.save("runs/debate_001")                     # writes conversation.json (recipe) + transcript.json
resumed = Conversation.load("runs/debate_001", devices="cuda")   # rebuilds lazy participants, ATTACHES the transcript
resumed.run(turns=4)                             # continues from where it left off (does not regenerate)

load takes devices= as a single device or a list (participants are round-robined across the list — handy for putting two big models on two GPUs), and raises on an unsupported (older) schema rather than silently mis-reading it.

Next: tools · hooks · interpretability · rollouts.