Configuration¶
All configuration is done through MnesisConfig, which groups settings into sub-configs. Every field has a sensible default — you only need to override what you want to change.
from mnesis import MnesisSession, MnesisConfig, CompactionConfig, FileConfig, StoreConfig, OperatorConfig
config = MnesisConfig(
compaction=CompactionConfig(...),
file=FileConfig(...),
store=StoreConfig(...),
operators=OperatorConfig(...),
)
session = await MnesisSession.create(model="openai/gpt-4o", config=config)
CompactionConfig¶
Controls when and how context compaction fires.
| Field | Default | Description |
|---|---|---|
auto |
True |
Auto-trigger compaction on overflow |
compaction_output_budget |
20_000 |
Tokens reserved as headroom for compaction summary output. Range 1,000-100,000. Usable context is context_limit - max_output_tokens - compaction_output_budget, and summaries (including the Level 3 fallback) are sized against it, so it must be well below your model's window. Session creation/loading raises ValueError when the budget leaves no usable context (context_limit - max_output_tokens <= compaction_output_budget, e.g. a small model_overrides window) and logs a compaction_output_budget_exceeds_usable_window warning when the budget is at least as large as the usable window. |
prune |
True |
Run tool output pruning before compaction |
prune_protect_tokens |
40_000 |
Token window from the end of history that is never pruned |
prune_minimum_tokens |
20_000 |
Minimum prunable volume required before pruning fires |
compaction_model |
None |
Model for summarisation. None = use session model |
level2_enabled |
True |
Attempt Level 2 compression before falling back to Level 3 |
condense_skip_level1 |
False |
Skip Level 1 when condensing summaries and start at Level 2. Use it for a compaction model known to overrun its output limit on the condensation prompt (e.g. Nemotron 3 Super: its condense L1 hit the cap in 4 of 4 samples, while condense L2 scored 9/9 on the recall probe). Ignored when level2_enabled is False. Independent of this flag, an engine skips condense L1 for the rest of its lifetime after two consecutive condense L1 completions are cut off at the output limit (one truncation can be a one-off; an accepted L1 condensation resets the count); that state is in memory only and is not persisted across session reloads. |
compaction_prompt |
None |
Custom prompt string for Level 1/2 LLM summarisation. None = use the built-in agentic prompt |
soft_threshold_fraction |
0.6 |
Fraction of usable context at which background compaction triggers (before hard threshold). Measured against the size of the current context window, not lifetime token usage. Also sets condensation's stop target: after summarising, summaries are condensed until the context is below half this threshold (soft_threshold_fraction * 0.5 of usable; the 0.5 is not configurable). Advanced. |
max_compaction_rounds |
10 |
Upper bound on condensation rounds per run, and on summarisation passes when the summariser's input cap (75% of the compaction model's window) forces several passes. Each condensation round merges all live summary nodes into one, so a run condenses at most once in practice. Advanced. |
condensation_enabled |
True |
Whether to attempt condensation of accumulated summary nodes. Advanced. |
Sizing soft_threshold_fraction for a slow compaction model¶
Background compaction starts when the context crosses the soft threshold, but the
conversation keeps going while it runs, so the context keeps growing until the
summary is swapped in. The room between the soft threshold and the hard limit
(1 - soft_threshold_fraction of usable) must cover what a turn adds times the
number of turns a compaction spans; otherwise the next send() blocks on the hard
limit (a compaction retry backoff makes a run longer still).
Measured in a real-model run (Nemotron 3 Super via OpenRouter, window 20,480, usable 16,432, soft 60% = 9,859, about 1,500 tokens or 9% of usable per turn of about 4 s): background runs took 8 to 37 s and spanned 2 to 5 turns, and the context peaked at 82% and 85% of the hard limit before the swap landed, with no blocking send. Rule of thumb:
Lower the fraction (for example 0.5) for a slow compaction_model, large turns,
or RetryConfig.max_retries > 0 with long backoffs; keep the default for fast
models. Mnesis does not chain runs or re-trigger while one is in flight (a run
already works from the live history, and starting a second would only duplicate
it), so this setting is the lever. CompactionConfig.compaction_model can point at
a faster model than the session model.
Tuning for large models¶
For models with 1M+ token contexts (e.g. Gemini 1.5 Pro), raise the budget and protect window:
CompactionConfig(
compaction_output_budget=100_000,
prune_protect_tokens=200_000,
prune_minimum_tokens=50_000,
)
Small windows: when session creation raises¶
MnesisSession.create() and load() raise ValueError whenever
context_limit - max_output_tokens <= compaction_output_budget, whether the
budget was set explicitly or left at the default (20,000). This is a breaking
change for model_overrides users with small windows. Those configs were
already unusable before the check: no history fit next to the reserved headroom,
so context_for_next_turn() returned a single message after four turns and
compaction ran and blocked every turn. Mnesis now fails fast instead.
Overrides that raise with the default budget (max_output_tokens is inherited
from the model string unless you set it):
| Model | model_overrides |
max_output_tokens |
Usable (limit - out - 20,000) |
|---|---|---|---|
ollama/llama3 |
context_limit=8_192 |
inherited | negative |
ollama/llama3 |
context_limit=8_192, max_output_tokens=2_048 |
2,048 | -13,856 |
ollama/llama3 |
context_limit=16_384 |
inherited | negative |
gpt-4o |
context_limit=32_768 |
16,384 (inherited) | -3,616 |
anthropic/claude-opus-4-6 |
context_limit=50_000 |
32,000 (inherited) | -2,000 |
o3-mini |
context_limit=120_000 |
100,000 (inherited) | 0 |
Fix either side of the inequality: lower compaction_output_budget (it only
needs to cover the summary the compaction model writes, so a few thousand
tokens suit a small window), or set max_output_tokens in model_overrides
to the reply length you actually need instead of inheriting the model's maximum:
MnesisConfig(
model_overrides={"context_limit": 32_768, "max_output_tokens": 4_096},
compaction=CompactionConfig(compaction_output_budget=4_000),
)
A run that blocks send() at the hard limit stops once the context is under
the soft threshold (leaving the rest for the background run; a bounded extra run
covers a per-turn system_prompt that is larger than the session prompt); a background run can make up to 2 * max_compaction_rounds sequential
summariser calls in the worst case (20 with the default), typically 1 to 2.
Custom compaction prompt¶
CompactionConfig(
compaction_prompt="Summarise this conversation focusing on technical decisions only.",
)
FileConfig¶
Controls how large files are handled.
| Field | Default | Description |
|---|---|---|
inline_threshold |
10_000 |
Files estimated above this token count are stored as FileRefPart objects |
storage_dir |
~/.mnesis/files/ |
Directory for external file storage. Defaults to ~/.mnesis/files/ |
exploration_summary_model |
None |
Reserved for future LLM-based structural summaries (AST, key lists, headings). Currently ignored — structural exploration summaries are generated deterministically only. |
StoreConfig¶
Controls the SQLite persistence layer.
| Field | Default | Description |
|---|---|---|
db_path |
~/.mnesis/sessions.db |
Path to the SQLite database file (~ is expanded at runtime) |
wal_mode |
True |
Use WAL journal mode for better concurrent read performance |
connection_timeout |
30.0 |
Seconds to wait for the database connection |
OperatorConfig¶
Controls LLMMap and AgenticMap parallelism.
| Field | Default | Description |
|---|---|---|
llm_map_concurrency |
16 |
Maximum concurrent LLM calls in LLMMap.run() |
agentic_map_concurrency |
4 |
Maximum concurrent sub-agent sessions in AgenticMap.run() |
max_retries |
3 |
Per-item retry attempts on validation or transient errors |
SessionConfig¶
Controls session-level behaviour.
| Field | Default | Description |
|---|---|---|
doom_loop_threshold |
3 |
Consecutive identical tool calls before DOOM_LOOP_DETECTED fires |
retry |
RetryConfig() |
Automatic retry configuration for transient LLM errors in send() |
RetryConfig¶
Controls automatic retry of transient LLM errors inside send(). Retry is opt-in: the default max_retries=0 preserves the pre-0.3.0 behaviour of failing immediately.
| Field | Default | Description |
|---|---|---|
max_retries |
0 |
Maximum retry attempts. 0 disables retry entirely |
base_delay |
1.0 |
Base delay in seconds for exponential backoff |
max_delay |
60.0 |
Maximum delay cap in seconds |
jitter |
True |
Add random jitter (sampled from [0, base_delay)) to spread retries |
Backoff formula: min(base_delay × 2^(attempt-1) + jitter, max_delay)
where attempt is the 1-based retry number matching LlmRetryPayload.attempt (first retry = 1, so the first backoff = base_delay × 2^0 = base_delay).
Retryable errors¶
Only transient provider-side errors are retried:
| litellm exception | HTTP status | Scenario |
|---|---|---|
RateLimitError |
429 | Provider rate limit |
InternalServerError |
500 | Provider server error |
ServiceUnavailableError |
503 | Provider temporarily down |
Timeout |
— | Request timed out |
APIConnectionError |
— | Network connection failed |
All other exceptions (including AuthenticationError, ContextWindowExceededError, BadRequestError) fail immediately without retry — they indicate caller mistakes that retrying will not fix.
litellm num_retries interaction¶
send() always passes num_retries=0 to litellm.acompletion(). For compaction calls: with RetryConfig.max_retries > 0, Mnesis retries compaction calls and disables LiteLLM retries (num_retries=0) to avoid double-retrying; with max_retries == 0 (default), compaction uses LiteLLM/provider default retries. Do not set num_retries in call_kwargs passed to litellm alongside Mnesis.
With max_retries > 0, compaction calls (summarisation and condensation, Levels 1 and 2) use the same attempts, backoff and error classification as send(): a transient 429/5xx is retried at the same level before escalating, instead of dropping straight to a lossier level. If a call outlasts its retries on a retryable error (an outage), the rest of that compaction run skips its remaining LLM levels and falls to the deterministic level (or skips condensation), so a run sleeps through at most max_retries backoffs of up to max_delay each, not one sequence per level. Non-retryable failures (auth errors, empty or truncated completions) still escalate level by level. Because a hard-limit send() waits for compaction, it can wait through that time, and so can session.close(), which waits for an in-flight compaction including its retry backoffs. To bound the wait, keep max_retries and max_delay small, or call session.close(abort_compaction=True), which ends an in-flight compaction's retry backoff at once (the run returns the failed stub, COMPACTION_FAILED with aborted=True; summaries from earlier rounds stay, and the history it was working on is left for the next session). Cancelling a task that is waiting on compaction (for example asyncio.timeout() around send()) does not cancel the compaction: it keeps running in the background and close() still drains it. Each compaction retry publishes LLM_RETRY with source="compaction" plus the stage and compaction_level of the call.
Difference from OperatorConfig.max_retries¶
OperatorConfig.max_retries handles per-item validation errors inside LLMMap and AgenticMap operators — a different failure domain (schema validation, JSON parse failures). RetryConfig handles transient LLM transport errors in the send() call path. Both can be set independently.
LLMMap also retries transport errors per item with OperatorConfig.max_retries (exponential backoff). When that is > 0 it passes num_retries=0 to LiteLLM so a failing call is not retried by both layers (up to (max_retries + 1) x LiteLLM's default attempts otherwise); with max_retries=0 LiteLLM's default retries apply. AgenticMap sub-sessions go through send(), which already disables LiteLLM retries.
AgenticMap sub-sessions¶
Each AgenticMap sub-session has its own independent RetryConfig. The effective maximum LLM calls per sub-agent is (max_retries + 1) × max_turns. Plan capacity accordingly.
Example¶
from mnesis import MnesisSession, MnesisConfig
from mnesis.models.config import SessionConfig, RetryConfig
config = MnesisConfig(
session=SessionConfig(
retry=RetryConfig(
max_retries=3,
base_delay=1.0,
max_delay=30.0,
jitter=True,
)
)
)
async with MnesisSession.open(model="openai/gpt-4o", config=config) as session:
# send() will now automatically retry up to 3 times on rate limits,
# server errors, timeouts, and connection failures.
result = await session.send("Hello!")
Monitoring retries via the event bus¶
Each retry attempt, of a send() turn or of a compaction call, publishes an LLM_RETRY event before the backoff sleep (payload["source"] is "send" or "compaction"; compaction events also carry stage and compaction_level):
from mnesis.events.bus import MnesisEvent
from mnesis.events.payloads import LlmRetryPayload
def on_retry(event: MnesisEvent, payload: LlmRetryPayload) -> None:
print(
f"Retry {payload['attempt']}/{payload['max_retries']}: "
f"{payload['error_type']} — sleeping {payload['delay_seconds']:.1f}s"
)
session.event_bus.subscribe(MnesisEvent.LLM_RETRY, on_retry) # type: ignore[arg-type]
model_overrides¶
MnesisConfig.model_overrides lets you correct or override the context and
output token limits that Mnesis auto-detects from the model string. This is
useful when you are using a fine-tuned model, a custom deployment, or a model
that litellm does not yet know about.
Supported keys:
| Key | Type | Description |
|---|---|---|
context_limit |
int |
Total input + output token limit for the model |
max_output_tokens |
int |
Maximum tokens the model can generate per response |
Example — custom or fine-tuned model¶
from mnesis import MnesisSession, MnesisConfig
config = MnesisConfig(
model_overrides={
"context_limit": 128_000,
"max_output_tokens": 16_384,
}
)
async with MnesisSession.open(model="openai/acme-support-ft-v1", config=config) as session:
result = await session.send("Hello!")
print(result.text)
Example — correcting an underestimated limit¶
If Mnesis falls back to conservative defaults for a model it does not recognise, override just the field that is wrong without touching any other configuration:
config = MnesisConfig(
model_overrides={"max_output_tokens": 32_768}, # provider raised the output limit
)
The overrides are applied after ModelInfo.from_model_string() resolves the
base limits, so only the keys you specify are changed — the rest (encoding,
provider, etc.) are inferred normally. Both context_limit and
max_output_tokens affect how Mnesis sizes the compaction budget, so incorrect
values can cause over-limit contexts to reach the provider or leave headroom
unused. Always set them to match the model's true limits.
model_overrides applies to the session model only. If you configure a
separate compaction.compaction_model, overrides are not applied to it — the
compaction model's limits are always auto-detected from the model string.
Note
model_overrides only affects how Mnesis allocates the context budget and
compaction thresholds — it does not change how litellm routes the request.
You still need to configure litellm (API base, headers, etc.) separately
for non-standard endpoints. See LLM Providers for the full
list of supported providers and model string formats.