Skip to content

Configuration

Every setting has a working default except the token. You can set any of them from the environment, and most of them from code by passing an argument to init().

Resolution happens once, inside the first init() call that is not a no-op.

  1. The kill switch. METERGRAPH_DISABLED=1, or init(disabled=True), marks the SDK initialized and returns. Nothing else is read. A later init() cannot undo it.
  2. The init() argument, when you passed one.
  3. The environment variable.
  4. The built-in default.

Two details of step 2 and step 3 matter in practice.

init() is idempotent. The first call that gets past the token check wins, and every later call logs Metergraph init() was called more than once; the first configuration remains active. once and returns. wrap() calls init() for you if you have not called it, so a wrap() before your own init() silently locks in the environment-only configuration.

Python and TypeScript disagree about what an empty string means. Python resolves token, ingest_url, repository and environment with or, so passing "" falls through to the environment variable. TypeScript resolves them with ??, so "" is a value and wins. Pass None/undefined, not an empty string, when you mean “fall back”.

The Python SDK reads thirteen variables. The TypeScript SDK reads eleven. Ten are shared. The Read by column is load-bearing: setting a Python-only variable in a Node process has no effect at all, and says nothing about it.

VariableRead byTypeDefaultRange and clampingWhat it does
METERGRAPH_APP_TOKENBothstringnonemust be non-emptyYour ingest key, mg_ followed by 48 hex characters. Unset means capture never starts
METERGRAPH_INGEST_URLBothURL stringhttps://d2xus7mp8zdv6t.cloudfront.netnot validatedBase URL. The SDK appends /v1/ingest, /v1/ingest/sessions and /v1/config. Point it at your own server to self-host
METERGRAPH_DISABLEDBothliteralunsetonly the exact string 1 countsTurns the SDK into a no-op without removing code. true and yes do not disable it
METERGRAPH_ENVBothstringnonestored as-is, truncated to 512 bytes on ingestEnvironment label stamped on every row
METERGRAPH_REPOSITORYBothowner/namenonemust contain /, otherwise ignoredRepository identity. Enables the session-token exchange. Without it the SDK falls back to .metergraph/config.json discovery, then to legacy app-token ingestion
METERGRAPH_CAPTURE_TEXTBothboolean1 (on)0, false, no, off (any case) are off. Every other value, including the empty string, is onWhether prompt and completion text leaves your process
METERGRAPH_TEXT_MAX_BYTESBothinteger bytes1048576 (1 MiB)clamped at the bottom only, max(1, value). There is no upper clamp, so it can be raisedPer-field cap applied to the serialized request, the serialized response, and the serialized tool-call list, each independently
METERGRAPH_QUEUE_SIZEBothinteger rows2000max(1, value)In-memory rows held before the SDK starts dropping. It drops, it never blocks your call
METERGRAPH_BATCH_SIZEBothinteger rows100clamped to 1-1000Rows per ingest request
METERGRAPH_PATCH_STREAM_USAGEBothliteral1 (on)only the exact string 0 disables itAdds stream_options.include_usage to streaming OpenAI chat.completions requests that do not already set it. Without it the provider reports no token counts on a stream. Not applied when a gateway is detected, because a gateway emits its own usage event
METERGRAPH_FLUSH_SECONDSPython onlyfloat seconds5max(0.05, value)Delivery interval
METERGRAPH_CONFIG_POLL_SECONDSPython onlyfloat seconds30max(1.0, value)How often the SDK polls GET /v1/config
METERGRAPH_CONFIG_HARD_TTL_SECONDSPython onlyfloat seconds120max(poll_seconds, value)Age at which a cached config document stops being trusted and model_for() returns your default
METERGRAPH_FLUSH_MSTypeScript onlyinteger ms5000max(50, value)Delivery interval

Python names its timing settings in seconds. TypeScript has exactly one timing environment variable and it is named in milliseconds. The other two timing settings have no TypeScript environment variable at all.

SettingPythonTypeScript
Delivery intervalMETERGRAPH_FLUSH_SECONDS, default 5METERGRAPH_FLUSH_MS, default 5000
Config poll intervalMETERGRAPH_CONFIG_POLL_SECONDS, default 30No environment variable. The configPollMs option, default 30000
Config hard TTLMETERGRAPH_CONFIG_HARD_TTL_SECONDS, default 120No environment variable. The configHardTtlMs option, default 120000

METERGRAPH_FLUSH_SECONDS=5 in a Node process does nothing, and neither does METERGRAPH_CONFIG_POLL_SECONDS anywhere but Python.

init() never raises and never blocks. It returns None/void.

All arguments are keyword-only.

metergraph.init(
token=None,
ingest_url=None,
capture_text=None,
redact=None,
app_root=None,
repository=None,
skip_frames=None,
environment=None,
disabled=None,
text_max_bytes=None,
)
ArgumentTypeDefaultWhat it does
tokenstr | NoneMETERGRAPH_APP_TOKENIngest key. Without a token init() returns without marking itself initialized, so a later init() that supplies one still succeeds.
ingest_urlstr | NoneMETERGRAPH_INGEST_URL, then DEFAULT_INGEST_URLBase URL. Trailing slashes are stripped.
capture_textbool | NoneMETERGRAPH_CAPTURE_TEXT, then TrueProcess-wide content capture. route(), trace() and context() can narrow it further, but cannot widen it.
redactCallable[[str, str], str] | NoneNoneCalled as redact(text, kind) where kind is "request" or "response", before truncation. Return the replacement string. If your function raises, the field becomes the literal <redaction-failed> and the row is still sent.
app_rootstr | Noneos.getcwd()Resolved with os.path.realpath(). Only stack frames whose file lives under this path become attribution frames.
repositorystr | NoneMETERGRAPH_REPOSITORYowner/name. Ignored unless it contains /.
skip_frameslist[str] | NoneNoneExtra substrings that disqualify a stack frame, appended to the built-in list: site-packages, metergraph/_capture.py, concurrent/futures, threading.py.
environmentstr | NoneMETERGRAPH_ENVEnvironment label.
disabledbool | NoneNoneTrue makes the SDK a no-op for the life of the process.
text_max_bytesint | NoneMETERGRAPH_TEXT_MAX_BYTES, then 1048576Per-field byte cap, clamped to at least 1.

Queue size, batch size and flush interval are environment-only in Python.

text_max_bytes is not the number of bytes Metergraph retains. There are two independent limits and they are not the same number.

StageLimitApplies toSet by
Leaves your process1 MiB per field, defaultSerialized request, serialized response, serialized tool callsMETERGRAPH_TEXT_MAX_BYTES, floor of 1, no ceiling
Retained after ingest100 KiB per fieldrequest_text, response_textServer-side, not configurable

Truncation appends the marker \n<metergraph:truncated> and sets text_truncated on the row. See Captured fields and Content and privacy.

Narrowing capture below the process default

Section titled “Narrowing capture below the process default”

capture_text is the process-wide floor. Three scopes can turn content off for part of the program.

with metergraph.route("checkout.summarize", capture_text=False):
...
with metergraph.trace("nightly-batch", capture_text=False):
...

A row whose content_opted_in is exactly false has its request_json, request_text and response_text dropped at ingest, before anything durable is written, and its tool_calls reduced to identifiers and status.