# Copy this file to `.env` and fill in real values. `.env` is git-ignored and # must never be committed. # # cp .env.example .env # $EDITOR .env # # Document every environment variable the crate, its tests, or its examples # read — including optional ones — with a placeholder value and a comment # saying what it is for. `.env.example` is the contract; `.env` is the secret. # Log filter for the `tracing`/`env_logger` style ecosystem. # RUST_LOG=info # Backtraces for local debugging: 1 for a short backtrace, "full" for all frames. # RUST_BACKTRACE=1 # The authenticated OpenAI-compatible ladder router. Host-side calls default to # localhost; Compose installs the pinned router image and reaches it as # `http://ladder:6969/v1` on its private project network. MATH_AGENT_API_KEY=replace-with-router-key # MATH_AGENT_API_BASE_URL=http://localhost:6969/v1 # MATH_AGENT_MODEL=flash # MATH_AGENT_REASONING_MODEL=reasoning # The deepest ladder the router holds. Set it to `reasoning` to put the # inventor, reflection and weakener back on the ordinary tier. # MATH_AGENT_MAX_REASONING_MODEL=max-reasoning # The exact ladder policy and its upstream credentials. These defaults match a # sibling checkout at /home/enamakel/work/llm-ladder-router when this repository # is at /home/enamakel/work/riemann; use absolute paths elsewhere. # LADDER_CONFIG_PATH=../llm-ladder-router/config.toml # LADDER_ENV_FILE=../llm-ladder-router/.env # LADDER_PORT=6969 # The key each problem's memory server indexes on, separate from the run's own. # Cognee calls a model to extract entities before it will persist a document, so # the memory competes with the run for the same daily limit unless it is given # its own — and when that limit is reached, the ingest is dropped and the run is # told it stored. `docs/memory.md` has the day that cost two problems their # whole memory. Keep this independent from the router credential. OPENROUTER_MEMORY_API_KEY=replace-me # The Lean scribe's tier, which is a ladder like every other tier. The router's # `scribe` ladder is one rung reaching Mistral directly, because no marketplace # resells Leanstral — so `MISTRAL_API_KEY` belongs in the *router's* .env # (`LADDER_ENV_FILE`), not in this one, and no key for it is read here. # # Point this at `flash` to put the role on the run's default model. It will be # slow: measured against this repository's own kernel, Leanstral answers a # routine Mathlib lemma in one to four seconds where the default spends three # minutes and still misses. Leanstral is free while it is in public preview, and # enabling it is an organisation setting on Mistral's *privacy* page, which is # to say prompts sent to it may be used as training data. Do not point it at an # argument this run has not published. # MATH_AGENT_SCRIBE_MODEL=scribe # Requests per second and in-flight calls allowed against that tier, applied # here rather than left to the router. The measured account ceiling is 0.63 # req/s and 5M tokens/min; above it the account returns 429 — and the router # answers a 429 by parking the rung, which on a one-rung ladder leaves nothing # to fall to. Queueing under the ceiling is cheaper than crossing it. # MATH_AGENT_SCRIBE_RPS=0.63 # MATH_AGENT_SCRIBE_CONCURRENCY=1 # How many blueprint nodes one pass of the verification arm hands to the kernel. # It was effectively one until the scribe tier made an attempt cheap; Conway-99 # ranks 87 candidates and four were ever attempted. # MATH_AGENT_VERIFY_BATCH=8 # What the inventor's workspace dossier may cost, in tokens. It is assembled # from disk at delegation time and packed in priority order, so lowering this # drops the lowest-priority sections rather than failing. # MATH_AGENT_DOSSIER_TOKENS=16000 # Exa web search used by the hello-world agent's `exa_search` tool. EXA_API_KEY=replace-me # Where the shared stack runs. Leave both empty for a single-box deployment, # where `./agent` brings the stack up here and the runs reach it through the # host gateway. Set them to put Neo4j, Cognee and the ladder on a machine of # their own — they are every problem's memory and none of its compute, so a box # that only holds them frees the run box for runs. The stack's own files live # beside it, in MATH_AGENT_SHARED_PATH on that host. # MATH_AGENT_SHARED_HOST=mac-mini # MATH_AGENT_SHARED_PATH=math-agent-shared # Which memory engine the box runs. `cortex` (the default) is one CortexDB, # carrying its own graph and its own vector index. `cognee` is the alternative: # a Cognee plus the Neo4j Enterprise instance it stores its graph in. It has to # be the same value for `scripts/shared-up` and for the runs — the launchers # read it once and bring up the matching half — and switching it does not move # what is already stored, so a problem continued under the other engine starts # from an empty memory. # MATH_AGENT_MEMORY=cortex # Where the shared stack answers, from inside a run container. The defaults # name the host gateway, which is the single-box case; point them at a tailnet # address when the stack is elsewhere. What keeps that safe is the memory # credential rather than the network — see docs/runtime.md. # # One variable, two servers: CortexDB listens on 3141 and Cognee on 8000, so an # address pinned here for one engine is wrong for the other. # `scripts/memory-up` checks that the address answers the selected engine's # readiness endpoint and refuses to print it otherwise, which is what turns # that mistake into a message rather than a run with no memory. # MATH_AGENT_MEMORY_URL=http://100.88.51.108:3141 # MATH_AGENT_API_BASE_URL=http://100.88.51.108:6969/v1 # CortexDB's one operator credential, shared by the whole deployment. Unlike # the Cognee tenant below this IS set here, because the self-hosted image # cannot mint scoped tokens: there is one key for the server, and what keeps # one problem out of another's memory is the scope the runtime builds from the # workspace label. `src/orchestrator/cortex.rs` states what that is worth. # `compose.shared.yaml` defaults it to a fixed local literal, on the same # footing as NEO4J_AUTH — set a real one before widening CORTEX_BIND_ADDRESS # past loopback. # CORTEX_API_KEY= # The scope subtree this deployment owns. Two deployments sharing one CortexDB # set this differently, or their problems collide by workspace slug. # CORTEX_SCOPE_ROOT=ws:math-agent # Where CortexDB publishes, on the box it runs on, and its ceilings. It holds # every problem's memory, so these are the whole shared budget. The resident # vector watermark is the one worth knowing about: the hot tier is uncapped by # default and the server warns about it at startup, at roughly 1.1 KB a vector. # CORTEX_BIND_ADDRESS=127.0.0.1 # CORTEX_PORT=3141 # CORTEX_MEM_LIMIT=16g # CORTEX_VECTOR_RESIDENT_MAX=4000000 # Under MATH_AGENT_MEMORY=cognee only: this problem's tenant on that server, # sent as an `X-Api-Key` header on every request. Do NOT set it here: it is per # problem, and `scripts/memory-up --key ` provisions and # prints it, which is what the launchers call. A value here would hand every # problem one tenant, and with it every other problem's memory. # COGNEE_API_KEY= # Memory ceilings for the shared Cognee and the shared Neo4j, which run only # under MATH_AGENT_MEMORY=cognee (compose.shared.yaml). One of each for every problem on the box, so these are # the whole shared budget rather than one problem's share — the per-problem # shape they replaced reserved 2g of Cognee each, twenty for ten problems. Both # also reserve 1g, which is the floor the scheduler holds for them while they # are idle between a run's turns. A repeated OOM kill on either stops every # problem's memory, so read `docker events --filter event=oom` before raising # one. # COGNEE_MEM_LIMIT=16g # NEO4J_MEM_LIMIT=16g # Where the stack publishes, on the box it runs on. Loopback by default, so an # ordinary deployment publishes to itself and nowhere else; set these to a # tailnet address when the runs are on a different machine. # COGNEE_BIND_ADDRESS=127.0.0.1 # LADDER_BIND_ADDRESS=127.0.0.1 # Neo4j's own sizing, which has to fit inside the host's Docker VM: a heap # larger than the VM is a startup failure rather than a slow server. # NEO4J_HEAP=4g # NEO4J_PAGECACHE=2g # LADDER_MEM_LIMIT=512m # CPU ceilings for the same two, out of the box's cores. They also carry twice # the default scheduling weight (`cpu_shares: 2048`), which is the half that # matters: run containers are not CPU bounded, so without it the shared # services compete against a dozen of them at equal priority and lose by sheer # number. Raise these if ingest is the bottleneck — `docker stats` on # `math-agent-shared-cognee-1` during a cognify says whether it is. # COGNEE_CPUS=4 # NEO4J_CPUS=4 # Memory ceiling for one run's container, and its RAM+swap total. One per run # and several runs at once, so a ceiling that overcommits by itself # overcommits by the number of them. 250m is reserved per container. Meeting # this cap is a finding about the method — stream rather than materialise — # and not a reason to raise it; raise it for one deliberately heavy run # instead, in the environment rather than here. # MATH_AGENT_MEM_LIMIT=4g # MATH_AGENT_MEMSWAP_LIMIT=8g # CPU ceiling for one run: 4 vCPU, hard capped. The matching floor — a tenth of # a core — is not expressible outside Swarm (Compose silently drops a cpu # reservation), so it is a low `cpu_shares: 256` instead: a run yields under # contention and reaches its full four whenever the cores are free. # MATH_AGENT_CPUS=4 # The wrappers set MATH_AGENT_WORKSPACE_LABEL. Cognee uses it to isolate # automatic session-memory datasets by project and runtime run. # Direct Langfuse ingestion used for TinyAgents loop observations. The # orchestrator and every specialist run export to it, with model and tool # payloads captured, so a trace shows the whole delegation tree. LANGFUSE_BASE_URL=https://langfuse.example.com LANGFUSE_PUBLIC_KEY=replace-me LANGFUSE_SECRET_KEY=replace-me # Run budget. Each is optional; an unset, empty, unparsable, or zero value keeps # the built-in default. Prefer the `./agent` and `./euler` flags where they # exist. See `src/agent/budget.rs`. # Model calls allowed per agent run before it stops with partial results (250). # MATH_AGENT_MAX_MODEL_CALLS=250 # Tool calls allowed per agent run (4000). Kept out of reach of the model-call # cap on purpose; only that cap stops gracefully. # MATH_AGENT_MAX_TOOL_CALLS=4000 # Wall-clock ceiling for one agent run, in minutes (120). # MATH_AGENT_RUN_MINUTES=120 # Wall-clock ceiling for the whole solution loop, in minutes (240). Distinct # from MATH_AGENT_RUN_MINUTES above, which bounds one agent run: that one kills # a stalled agent and leaves the loop that spawned it running. This is the only # terminal condition that does not count events, so it is what stops a loop # whose arms all die before they can return. Lower it for Project Euler, raise # it for a conjecture run. # MATH_AGENT_LOOP_MINUTES=240 # Safety ceiling on output tokens per model turn (12000). Must stay above a # normal turn: a cap that trips truncates the model and forces a retry. # MATH_AGENT_TURN_OUTPUT_TOKENS=12000 # Wall-clock ceiling for one tool call, in minutes (10). # MATH_AGENT_TOOL_MINUTES=10 # The shared brief, `CONTEXT.md`, is routed into nearly every reasoning role's # system prompt, so it is re-sent on every model call those roles make. These # two say what it may cost and how often it is refreshed. See # `src/orchestrator/shared_context.rs`. # Token budget for CONTEXT.md (10000). The curator is briefed against it each # cycle, and a brief over budget is cut where it exceeds this on the way into a # prompt, with a notice saying so. # MATH_AGENT_CONTEXT_TOKENS=10000 # Ceiling every file routed into a system prompt is held to (10000), whatever # kind it is. Applied after the brief's budget and the per-ledger budget, so it # never pre-empts a bound that can cut more intelligently. It should never fire: # a file that reaches it is one nothing else is bounding. # MATH_AGENT_PROMPT_FILE_TOKENS=10000 # Minutes between context-curator cycles (5). A cycle with nothing new to say # costs a directory walk rather than a model call. # MATH_AGENT_CONTEXT_MINUTES=5 # Spawned agent runs allowed to execute at once (50). The rest queue; spawning # stays non-blocking. Keep it well above the real fan-out — a run holds its # slot while awaiting children it spawned itself. # MATH_AGENT_MAX_CONCURRENT_AGENTS=50 # Set to `off` to withhold `exa_search` from the research agent, making a run a # test of reasoning rather than of lookup. Equivalent to `--no-research`. # MATH_AGENT_RESEARCH=on # Set to `off` to withhold `map_document`, the chunked recursive read that # answers a question about a whole document with one model call per chunk. # Equivalent to `--no-rlm`. The outline, the selected read and `grep_workspace` # are unaffected, so a large document stays readable; what a run loses is the # ability to ask one question of a whole file. Off is worth choosing when the # per-chunk calls cost more than the answer is worth, or when a calibration run # is being measured without them. # MATH_AGENT_RLM=on # The bounds of the reading layer, in bytes. The first is how much of a # document an unselected `read_document` returns before answering with the # outline instead; the second is how much one `section` or `lines` read may # carry, and is held at or above the first. Raise them for a run with a large # context window; they are a judgement, not a limit of the format. # MATH_AGENT_READ_CEILING=24576 # MATH_AGENT_READ_SLICE=49152 # What one `map_document` call does: how much source each chunk read sees, how # many chunks one call will read before it stops and says so, and how many of # those calls are in flight at once. Concurrency shares the container's one # provider connection pool with the run waiting on the tool. # MATH_AGENT_RLM_CHUNK_BYTES=24576 # MATH_AGENT_RLM_MAX_CHUNKS=60 # MATH_AGENT_RLM_CONCURRENCY=6 # The shared Neo4j (`compose.shared.yaml`) publishes the Browser on 7474 and # Bolt on 7687, bound to the loopback interface by default. Set both of these # to a Tailscale address to read a problem's graph from another of your own # machines; they must match, or the Browser loads and hangs connecting. # NEO4J_BIND_ADDRESS=127.0.0.1 # NEO4J_ADVERTISED_ADDRESS=localhost