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().
Precedence
Section titled “Precedence”Resolution happens once, inside the first init() call that is not a no-op.
- The kill switch.
METERGRAPH_DISABLED=1, orinit(disabled=True), marks the SDK initialized and returns. Nothing else is read. A laterinit()cannot undo it. - The
init()argument, when you passed one. - The environment variable.
- The built-in default.
- The kill switch.
METERGRAPH_DISABLED=1, orinit({ disabled: true }), marks the SDK initialized and returns. Nothing else is read. A laterinit()cannot undo it. - The
init()option, when you passed one. - The environment variable.
- 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”.
Environment variables
Section titled “Environment variables”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.
| Variable | Read by | Type | Default | Range and clamping | What it does |
|---|---|---|---|---|---|
METERGRAPH_APP_TOKEN | Both | string | none | must be non-empty | Your ingest key, mg_ followed by 48 hex characters. Unset means capture never starts |
METERGRAPH_INGEST_URL | Both | URL string | https://d2xus7mp8zdv6t.cloudfront.net | not validated | Base URL. The SDK appends /v1/ingest, /v1/ingest/sessions and /v1/config. Point it at your own server to self-host |
METERGRAPH_DISABLED | Both | literal | unset | only the exact string 1 counts | Turns the SDK into a no-op without removing code. true and yes do not disable it |
METERGRAPH_ENV | Both | string | none | stored as-is, truncated to 512 bytes on ingest | Environment label stamped on every row |
METERGRAPH_REPOSITORY | Both | owner/name | none | must contain /, otherwise ignored | Repository 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_TEXT | Both | boolean | 1 (on) | 0, false, no, off (any case) are off. Every other value, including the empty string, is on | Whether prompt and completion text leaves your process |
METERGRAPH_TEXT_MAX_BYTES | Both | integer bytes | 1048576 (1 MiB) | clamped at the bottom only, max(1, value). There is no upper clamp, so it can be raised | Per-field cap applied to the serialized request, the serialized response, and the serialized tool-call list, each independently |
METERGRAPH_QUEUE_SIZE | Both | integer rows | 2000 | max(1, value) | In-memory rows held before the SDK starts dropping. It drops, it never blocks your call |
METERGRAPH_BATCH_SIZE | Both | integer rows | 100 | clamped to 1-1000 | Rows per ingest request |
METERGRAPH_PATCH_STREAM_USAGE | Both | literal | 1 (on) | only the exact string 0 disables it | Adds 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_SECONDS | Python only | float seconds | 5 | max(0.05, value) | Delivery interval |
METERGRAPH_CONFIG_POLL_SECONDS | Python only | float seconds | 30 | max(1.0, value) | How often the SDK polls GET /v1/config |
METERGRAPH_CONFIG_HARD_TTL_SECONDS | Python only | float seconds | 120 | max(poll_seconds, value) | Age at which a cached config document stops being trusted and model_for() returns your default |
METERGRAPH_FLUSH_MS | TypeScript only | integer ms | 5000 | max(50, value) | Delivery interval |
The unit trap
Section titled “The unit trap”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.
| Setting | Python | TypeScript |
|---|---|---|
| Delivery interval | METERGRAPH_FLUSH_SECONDS, default 5 | METERGRAPH_FLUSH_MS, default 5000 |
| Config poll interval | METERGRAPH_CONFIG_POLL_SECONDS, default 30 | No environment variable. The configPollMs option, default 30000 |
| Config hard TTL | METERGRAPH_CONFIG_HARD_TTL_SECONDS, default 120 | No 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() arguments
Section titled “init() arguments”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,)| Argument | Type | Default | What it does |
|---|---|---|---|
token | str | None | METERGRAPH_APP_TOKEN | Ingest key. Without a token init() returns without marking itself initialized, so a later init() that supplies one still succeeds. |
ingest_url | str | None | METERGRAPH_INGEST_URL, then DEFAULT_INGEST_URL | Base URL. Trailing slashes are stripped. |
capture_text | bool | None | METERGRAPH_CAPTURE_TEXT, then True | Process-wide content capture. route(), trace() and context() can narrow it further, but cannot widen it. |
redact | Callable[[str, str], str] | None | None | Called 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_root | str | None | os.getcwd() | Resolved with os.path.realpath(). Only stack frames whose file lives under this path become attribution frames. |
repository | str | None | METERGRAPH_REPOSITORY | owner/name. Ignored unless it contains /. |
skip_frames | list[str] | None | None | Extra substrings that disqualify a stack frame, appended to the built-in list: site-packages, metergraph/_capture.py, concurrent/futures, threading.py. |
environment | str | None | METERGRAPH_ENV | Environment label. |
disabled | bool | None | None | True makes the SDK a no-op for the life of the process. |
text_max_bytes | int | None | METERGRAPH_TEXT_MAX_BYTES, then 1048576 | Per-field byte cap, clamped to at least 1. |
Queue size, batch size and flush interval are environment-only in Python.
init() takes one options object. Every field is optional.
mg.init({ token: undefined, ingestUrl: undefined, captureText: undefined, redact: undefined, appRoot: undefined, repository: undefined, skipFrames: undefined, environment: undefined, disabled: undefined, transport: undefined, queueSize: undefined, batchSize: undefined, flushMs: undefined, configPollMs: undefined, configHardTtlMs: undefined, textMaxBytes: undefined,});| Option | Type | Default | What it does |
|---|---|---|---|
token | string | METERGRAPH_APP_TOKEN | Ingest key. Without one, init() returns uninitialized so a later call can still supply it. |
ingestUrl | string | METERGRAPH_INGEST_URL, then DEFAULT_INGEST_URL | Base URL. A trailing slash is stripped. |
captureText | boolean | METERGRAPH_CAPTURE_TEXT, then true | Process-wide content capture. |
redact | (text: string, kind: "request" | "response") => string | none | Runs before truncation. |
appRoot | string | process.cwd() | Attribution root for stack frames. |
repository | string | METERGRAPH_REPOSITORY | owner/name. Ignored unless it contains /. |
skipFrames | string[] | [] | Extra substrings that disqualify a stack frame. |
environment | string | METERGRAPH_ENV | Environment label. |
disabled | boolean | false | true makes the SDK a no-op for the life of the process. |
transport | "auto" | "background" | "buffered" | "auto" | Delivery strategy. "auto" picks "buffered" when AWS_LAMBDA_FUNCTION_NAME or VERCEL is set, or when navigator.userAgent is exactly Cloudflare-Workers. Otherwise "background". |
queueSize | number | METERGRAPH_QUEUE_SIZE, then 2000 | Rows held in memory, max(1, value). |
batchSize | number | METERGRAPH_BATCH_SIZE, then 100 | Rows per request, clamped to 1–1000. |
flushMs | number | METERGRAPH_FLUSH_MS, then 5000 | Background flush interval, max(50, value). Ignored in "buffered" mode, which flushes on enqueue. |
configPollMs | number | 30000 | Config poll interval. There is no environment variable for this. |
configHardTtlMs | number | 120000 | Age at which cached config is ignored. There is no environment variable for this. |
textMaxBytes | number | METERGRAPH_TEXT_MAX_BYTES, then 1048576 | Per-field byte cap, max(1, floor(value)). A non-finite value falls back to the default. |
Content caps, in two stages
Section titled “Content caps, in two stages”text_max_bytes is not the number of bytes Metergraph retains. There are two
independent limits and they are not the same number.
| Stage | Limit | Applies to | Set by |
|---|---|---|---|
| Leaves your process | 1 MiB per field, default | Serialized request, serialized response, serialized tool calls | METERGRAPH_TEXT_MAX_BYTES, floor of 1, no ceiling |
| Retained after ingest | 100 KiB per field | request_text, response_text | Server-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): ...await mg.route("checkout.summarize", async () => { // ...}, { captureText: false });
await mg.trace("nightly-batch", async () => { // ...}, { captureText: 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.
Related
Section titled “Related”- Python SDK and TypeScript SDK for the full call surface.
- Ingest API for what the SDK is talking to.
- Limits and allowances for the caps you cannot configure.
- Errors for every warning string above.