---
title: "Attribute keys and mappers"
sidebar:
  label: "Attribute keys and mappers"
description: "The judgment.* span attribute keys Judgment reads, and how attribute mappers fill them at ingest."
seo:
  title: "Attribute keys and mappers | Judgment Docs"
  description: "The judgment.* span attribute keys Judgment reads, and how attribute mappers fill them at ingest."
---

Judgment reads the `judgment.*` span attributes below as structured fields. Any other key is stored as a custom attribute and stays searchable.

Columns:

- **Mapper target**: whether an attribute mapper can write the key. Keys marked No are set by the SDKs or the ingest pipeline.
- **Set by SDK helper**: the judgeval helper that writes the key, as `python_name` / `typescriptName`, checked against judgeval 1.3.3 (Python) and 1.3.4 (npm). None means no SDK helper writes it; set it with `Tracer.set_attribute` / `Tracer.setAttribute`, a raw OpenTelemetry SDK, or a mapper. `wrap()` here means the OpenAI wrapper.

The Agent keys and `judgment.state_before` / `judgment.state_after` are declared in both SDKs, but no helper in the current SDK releases writes them, even where a description says the SDK sets them automatically.

## Span attribute keys

### Core

| Key | Mapper target | Set by SDK helper | Description |
| --- | --- | --- | --- |
| `judgment.input` | Yes | `observe` (automatic), `set_input` / `setInput` | The input a step receives. |
| `judgment.output` | Yes | `observe` (automatic), `set_output` / `setOutput` | The output a step produces. |
| `judgment.llm.tool_calls` | Yes | None | Structured tool/function calls made during a step. |
| `judgment.span_kind` | Yes | `observe(span_type=...)` / `observe(fn, { spanType })`, `set_span_kind` / `setSpanKind`, `wrap()` | The type of step (`llm`, `tool`, `function`, `agent`, ...). Judgment infers this automatically when usage or provider/model attributes are present, so you rarely need to set it yourself. |
| `judgment.span_name` | Yes | None | The name shown for this step in the trace. Falls back to the raw OpenTelemetry span name when unset. |
| `judgment.agent_version` | Yes | None | The version of the agent or prompt implementation used for this step. Powers the agent-version breakdown in monitoring. |

### Identifiers

| Key | Mapper target | Set by SDK helper | Description |
| --- | --- | --- | --- |
| `judgment.session_id` | Yes | `set_session_id` / `setSessionId` | Ties multiple traces to one conversation or session. Map here to view all of a user's requests together. |
| `judgment.customer_id` | Yes | `set_customer_id` / `setCustomerId` | Which of your customers (tenant/account) a trace belongs to. Map here to filter and break down activity by customer. |
| `judgment.customer_user_id` | Yes | `set_customer_user_id` / `setCustomerUserId` | Which individual end user a trace belongs to. Map here to trace activity down to a single user. |
| `judgment.project_id` | No | None | The Judgment project a trace belongs to. Judgment sets this from your API key/SDK configuration; it is not a mapper target. |
| `judgment.test.run_id` | No | `test_case` / `testCase` | The test run a trace belongs to. Read from the trace's root span, together with `judgment.test.case_id`, to attach the trace to its test case. |
| `judgment.test.case_id` | No | `test_case` / `testCase` | The test case within `judgment.test.run_id` that a trace answers. Read from the trace's root span; the first matching trace is used for the case. |

### Agent

| Key | Mapper target | Set by SDK helper | Description |
| --- | --- | --- | --- |
| `judgment.agent_id` | No | None | Identifies the agent instance that produced this span. Judgment SDK agent instrumentation sets this automatically; it powers agent grouping and is not a mapper target. |
| `judgment.parent_agent_id` | No | None | The agent ID of the caller, for spans produced by a sub-agent. Used to reconstruct multi-agent call hierarchies. |
| `judgment.agent_class_name` | No | None | The class/type name of the agent implementation, set automatically by SDK agent instrumentation. |
| `judgment.agent_instance_name` | No | None | The specific instance/display name of the agent, set automatically by SDK agent instrumentation. |
| `judgment.is_agent_entry_point` | No | None | Marks the span where an agent run begins, as opposed to an internal step of that run. |

### Trace Links

| Key | Mapper target | Set by SDK helper | Description |
| --- | --- | --- | --- |
| `judgment.link.target_trace_id` | No | `observe(..., fork=True)` / `fork: true` | The trace ID of a sub-agent (or otherwise related) trace this span links forward to, for cross-trace navigation. |
| `judgment.link.target_span_id` | No | `observe(..., fork=True)` / `fork: true` | The span ID within the linked target trace, paired with `judgment.link.target_trace_id`. |
| `judgment.link.source_trace_id` | No | `observe(..., fork=True)` / `fork: true` | The trace ID of the caller when this trace was started by another (e.g. a parent agent invoking a sub-agent). |
| `judgment.link.source_span_id` | No | `observe(..., fork=True)` / `fork: true` | The span ID within the linking source trace, paired with `judgment.link.source_trace_id`. |

### Usage & Cost

| Key | Mapper target | Set by SDK helper | Description |
| --- | --- | --- | --- |
| `judgment.llm.provider` | Yes | `recordLLMMetadata` (`provider`) | The model provider, e.g. OpenAI or Anthropic. Map here so Judgment can price token usage accurately. |
| `judgment.llm.model` | Yes | `recordLLMMetadata` (`model`), `wrap()` | The exact model used, e.g. gpt-4o. Map here (together with provider) so cost is calculated correctly. |
| `judgment.usage.non_cached_input_tokens` | Yes | `recordLLMMetadata` (`non_cached_input_tokens`), `wrap()` | Input tokens charged at the normal (uncached) rate. Map here to report input token cost. |
| `judgment.usage.cache_creation_input_tokens` | Yes | `recordLLMMetadata` (`cache_creation_input_tokens`) | Input tokens written into the prompt cache. Map here to track prompt-cache write cost. |
| `judgment.usage.reasoning_tokens` | Yes | None | Tokens a reasoning model spends "thinking". Map here to account for reasoning cost. |
| `judgment.usage.cache_read_input_tokens` | Yes | `recordLLMMetadata` (`cache_read_input_tokens`), `wrap()` | Input tokens read from the prompt cache at the cheaper cached rate. Map here to track cache savings. |
| `judgment.usage.output_tokens` | Yes | `recordLLMMetadata` (`output_tokens`), `wrap()` | Tokens the model generated in its response. Map here to report output token cost. |
| `judgment.usage.non_cached_input_image_tokens` | Yes | None | Image input tokens charged at the normal rate. Map here if your model bills image inputs separately. |
| `judgment.usage.output_image_tokens` | Yes | None | Image tokens the model generated. Map here if your model bills image outputs separately. |
| `judgment.usage.total_cost_usd` | Yes | `recordLLMMetadata` (`total_cost_usd`) | A total cost you have already computed for this step, in USD. Map here to report cost directly instead of from tokens. |

### State snapshots

| Key | Mapper target | Set by SDK helper | Description |
| --- | --- | --- | --- |
| `judgment.state_before` | No | None | A snapshot of the agent's state before this step ran, for evaluators/judges that score state transitions. |
| `judgment.state_after` | No | None | A snapshot of the agent's state after this step ran, paired with `judgment.state_before`. |

### Reserved keys

Other `judgment.*` keys are reserved for the Judgment SDKs and the ingest pipeline. Don't set them yourself or target them with a mapper.

## Resource attributes

Judgment reads these from the OpenTelemetry resource, not from span attributes.

| Resource attribute | Set by | Meaning |
| --- | --- | --- |
| `service.name` | `Tracer.init`, default: the project name | Service that emitted the span. Override with `resource_attributes` (Python) or `resourceAttributes` (TypeScript). |
| `deployment.environment.name` | `Tracer.init(environment=...)` | Environment of the trace. `deployment.environment` is read as a fallback. Empty shows as **No environment**. |
| `telemetry.sdk.name`, `telemetry.sdk.version` | `Tracer.init` | SDK identity. |
| `judgment.project_id` | judgeval Workers tracer | Project ID stamped on the resource. Ingest routes by the `X-Project-Id` header, not this attribute. |

## Built-in normalization

At ingest, built-in mappers copy common third-party attributes onto `judgment.*` keys. They cover OTel GenAI semantic conventions, OpenInference, LangSmith, Vercel AI SDK 6 and 7, Logfire, Braintrust cost, and generic LLM provider attributes.

| Incoming attribute | Judgment key |
| --- | --- |
| `gen_ai.response.model`, then `gen_ai.request.model` | `judgment.llm.model` |
| `gen_ai.system` | `judgment.llm.provider` |
| `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` | `judgment.usage.non_cached_input_tokens`, `judgment.usage.output_tokens` |
| `gen_ai.usage.cache_read_input_tokens`, `gen_ai.usage.cache_creation_input_tokens` | `judgment.usage.cache_read_input_tokens`, `judgment.usage.cache_creation_input_tokens` |
| `gen_ai.input.messages`, `gen_ai.prompt`, `gen_ai.tool.call.arguments` | `judgment.input` |
| `gen_ai.output.messages`, `gen_ai.completion`, `gen_ai.tool.call.result` | `judgment.output` |
| `gen_ai.conversation.id` | `judgment.session_id` |
| `gen_ai.tool.name` present | `judgment.span_kind` = `tool` |
| `gen_ai.operation.name` = `chat` or `text_completion` | `judgment.span_kind` = `llm` |

Add a configured mapper only for keys this list does not cover.

## Mapper order

Ingest runs mappers in five layers. Each layer sees the output of the layers before it.

| Order | Layer | What runs |
| --- | --- | --- |
| 1 | Platform normalization | The built-in mappers above. |
| 2 | Organization mappers | Org-wide configured mappers. |
| 3 | Project mappers | Mappers configured for the project. |
| 4 | Platform enrichment | Span kind inference and LLM cost calculation. |
| 5 | Conversation | Records model calls in the form the Conversation view reads. Runs only for organizations where conversation recording is enabled. |

Customer mappers run before span kind inference and cost calculation. A mapper that writes `judgment.llm.provider`, `judgment.llm.model`, and token counts therefore feeds cost calculation and `llm` span kind inference.

Span kind inference marks a span `llm` when it carries usage, provider, or model attributes for a billable GenAI operation. Rollup operations such as `invoke_agent` are excluded.

## Mapper settings

| Setting | Default | Behavior |
| --- | --- | --- |
| **Enabled** | On | Disabled mappers are skipped. |
| **Override** | Off | Off: writes the target only if it has no value yet. On: always writes the target. |
| **Remove Source** | On | Deletes the source key, only when the mapping was applied. |

- Targets must be keys with Mapper target = Yes.
- Mappers apply at ingest. Spans stored before a mapper existed do not change.
- Creating, editing, or deleting a mapper applies to spans ingested from then on.
- Org-wide mappers can only be edited by a Judgment administrator. Creating, editing, or deleting project mappers needs the developer role, in the app and through MCP.

## Manage mappers

In the app: open **Settings**, choose the project, then open **Mappers**.

{/* SCREENSHOT: Settings > Project > Mappers list with the New Mapper dialog open, dark mode */}

MCP tools: `list_mappers`, `get_mapper`, `create_mapper`, `update_mapper`, `delete_mapper`. See [MCP tools](/documentation/reference/mcp-tools) for inputs and roles.

