Skip to main content
Nori predicts in context, so your table is input: every call reads all of X_train and keeps a per-layer key/value cache over those rows. That cache — not the model itself — is what fills a GPU on a big table. Nori handles it by walking a ladder of fallbacks, keeping the cache at full precision while it fits and stepping down only when it must. memory_policy lets you decide which steps it may take, and tells you afterwards which one it used. The same fields work locally and over the hosted API.
Locally this needs synthefy-nori 0.13.0 or newer; see Installation.

Quickstart

Omit memory_policy entirely and nothing changes — the defaults are what almost every call should use. They are: cache at full precision (bf16), let it drop to int8 only if that is what keeps it on the GPU, spend at most 40% of VRAM on it, offload to host RAM rather than give up, and shrink the context only as a last resort. In short: stay exact while that is possible, and never fail outright if there is any way to serve the request. Reach for a policy when you want a different trade. A preset covers the common three:

Which lever saves the most memory

Ranked by how much GPU memory each one frees, most first. Figures are from a 400-row × 8-feature context, whose full-precision cache is 0.0122 GiB — yours scales with rows and features, but the ratios hold. Note that int8 is ~1.9× and not 4×: the saving is against bf16, not fp32, and the per-row scales cost a little back.

Row chunking

Use context_row_chunk when a call dies building the cache rather than holding it — the projection over all N rows at once is the spike, and this processes N a slice at a time. It is the lever for “it OOMs on fit”, where a smaller budget would not help.
Nori also reaches for this automatically after an out-of-memory retry, so you rarely have to set it — see the warning in Good to know about its numerics.

Capping the budget on a shared GPU

Budgets are fractions of your hardware by default, so one setting travels between cards. Use the absolute form when a hard ceiling is the actual requirement, such as a GPU you share with another process:

Refusing a shortened context

By default Nori will drop context rows rather than fail. When a quietly shortened context would invalidate your results, make it an error instead:
Either way, check memory_report_["dropped_context_rows"].

How it works

One predict reads the context once and saves a key/value vector per layer, per feature group, per context row. Query rows then attend to that cache instead of re-reading the table. The cache is what grows with your data:
Query rows are processed in batches, and the cache is what makes the second batch cheap. That is also why it is only built when there is a second batch — a query set small enough to fit one pass has nothing to reuse it across, and reports no_cache. That is normal, not a degradation. When the cache does not fit, Nori takes the cheapest step that works: Only the int8 rungs trade accuracy, and they are reached only when full precision will not fit. Offloading moves bytes rather than approximating.

Reading the report

memory_report_ locally, last_memory_report on the client, memory_report in the raw response. It carries the resolved policy plus what was decided: The last two are added by the server when it echoes the report, so they are present over the API and absent from memory_report_ locally. dropped_context_rows is the one number worth watching: it is the only accuracy loss here that is not a rounding-level effect. Set allow_subsample=False to make that case an error instead.

Reaching the cached path over the API

Coming soon. The cache is built only when your query set spans more than one batch, and today that threshold is more query rows than a single hosted request can carry — so a hosted call reports no_cache however much data you send. We are raising the hosted request limit; once it lifts, a normal-sized request reaches the cached path on its own, with nothing extra to set.

Configuration

Budgets are fractions of your hardware, so one setting travels from a laptop GPU to an H200. The *_absolute_gb overrides exist for a shared GPU, where a hard ceiling is the requirement rather than a share.Over the hosted API the host-RAM budgets are capped to what the container survives, and a cap is reported in clamped rather than applied silently.

Good to know

Incoherent settings fail loudly. Asking for a cache-only field alongside cache=False is rejected rather than ignored, because a policy spelled correctly that silently does nothing is worse than an error. Settings that work but probably are not what you meant are honoured and explained in notes.
The report is not a policy. Feeding a memory_report back in as memory_policy is rejected: those are decided outputs, and reusing them as configuration would skip the coherence checks.
context_row_chunk is not bit-identical. A single chunk reproduces the unchunked result exactly, but a smaller chunk differs at around 4e-3 on a 512-row query set — the projection’s accumulation order depends on the chunk size. It is reached automatically after an out-of-memory retry, so a call that retries can return slightly different numbers.
Not on Thinking models. A Nori Thinking deployment rejects memory_policy with a 400. It predicts through a test-time-compute wrapper that manages its own inference memory, so a policy passed in from outside would not reach the run that produces the answer — and accepting it silently would be worse than refusing it.
Where a cache cannot help. Preprocessing (polynomial features and the adaptive SVD) runs before any of this and has its own memory cost, which memory_policy does not govern. A table that fails there fails regardless of the policy.

Next steps

Hosted client

Auth, modes, and the request contract for calling Nori over the API.

Missing values

Pass NaNs straight through — what the model does with them.