Metadata-Version: 2.5
Name: amplitude-ai
Version: 1.26.0
Summary: Agent analytics for Amplitude
Project-URL: Homepage, https://amplitude.com
Project-URL: Documentation, https://amplitude.com/docs/sdks/agent-analytics/sdk
Author-email: Amplitude <sdk@amplitude.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,amplitude,analytics,anthropic,llm,observability,openai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: amplitude-analytics<2.0.0,>=1.0.0
Requires-Dist: genai-prices<1.0.0,>=0.1.3
Provides-Extra: all
Requires-Dist: anthropic<1.0.0,>=0.75.0; extra == 'all'
Requires-Dist: boto3<2.0.0,>=1.40.0; extra == 'all'
Requires-Dist: crewai<2.0.0,>=0.80.0; extra == 'all'
Requires-Dist: fastmcp<5.0.0,>=2.14.0; extra == 'all'
Requires-Dist: google-genai<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-core<2.0.0,>=0.1.0; extra == 'all'
Requires-Dist: llama-index-core<1.0.0,>=0.10.0; extra == 'all'
Requires-Dist: mistralai<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: openai-agents<1.0.0,>=0.0.3; extra == 'all'
Requires-Dist: openai<3.0.0,>=2.0.0; extra == 'all'
Requires-Dist: opentelemetry-api<2.0.0,>=1.20.0; extra == 'all'
Requires-Dist: opentelemetry-sdk<2.0.0,>=1.20.0; extra == 'all'
Requires-Dist: starlette<2.0.0,>=0.41.0; extra == 'all'
Requires-Dist: tiktoken<1.0.0,>=0.12.0; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic<1.0.0,>=0.75.0; extra == 'anthropic'
Provides-Extra: azure
Requires-Dist: openai<3.0.0,>=2.0.0; extra == 'azure'
Provides-Extra: bedrock
Requires-Dist: boto3<2.0.0,>=1.40.0; extra == 'bedrock'
Provides-Extra: crewai
Requires-Dist: crewai<2.0.0,>=0.80.0; extra == 'crewai'
Provides-Extra: dev
Requires-Dist: pytest-asyncio<2.0.0,>=0.24.0; extra == 'dev'
Requires-Dist: pytest<10.0.0,>=8.0.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material<10.0.0,>=9.5.0; extra == 'docs'
Requires-Dist: mkdocs<2.0.0,>=1.6.0; extra == 'docs'
Requires-Dist: mkdocstrings[python]<2.0.0,>=0.25.0; extra == 'docs'
Provides-Extra: fastapi
Requires-Dist: starlette<2.0.0,>=0.41.0; extra == 'fastapi'
Provides-Extra: gemini
Requires-Dist: google-genai<2.0.0,>=1.0.0; extra == 'gemini'
Provides-Extra: langchain
Requires-Dist: langchain-core<2.0.0,>=0.1.0; extra == 'langchain'
Provides-Extra: llamaindex
Requires-Dist: llama-index-core<1.0.0,>=0.10.0; extra == 'llamaindex'
Provides-Extra: mcp
Requires-Dist: fastmcp<5.0.0,>=2.14.0; extra == 'mcp'
Provides-Extra: mistral
Requires-Dist: mistralai<2.0.0,>=1.0.0; extra == 'mistral'
Provides-Extra: openai
Requires-Dist: openai<3.0.0,>=2.0.0; extra == 'openai'
Provides-Extra: openai-agents
Requires-Dist: openai-agents<1.0.0,>=0.0.3; extra == 'openai-agents'
Provides-Extra: otel
Requires-Dist: opentelemetry-api<2.0.0,>=1.20.0; extra == 'otel'
Requires-Dist: opentelemetry-sdk<2.0.0,>=1.20.0; extra == 'otel'
Provides-Extra: tokens
Requires-Dist: tiktoken<1.0.0,>=0.12.0; extra == 'tokens'
Description-Content-Type: text/markdown

<a id="amplitude-ai"></a>
# amplitude-ai

[![PyPI version](https://img.shields.io/pypi/v/amplitude-ai)](https://pypi.org/project/amplitude-ai/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Agent analytics for [Amplitude](https://amplitude.com). Track every LLM call, user message, tool call, and quality signal as events in your Amplitude project — then build funnels, cohorts, and retention charts across AI and product behavior alongside your existing product events.

> **1.17.0:** PII redaction is now on by default for `AmplitudeAI` / `AIConfig`. Read [Privacy](#privacy) before upgrading if you rely on raw message content.

<a id="-documentation"></a>
## 📘 Documentation

The canonical SDK reference lives in Amplitude Docs:

**[amplitude.com/docs/sdks/agent-analytics/sdk](https://amplitude.com/docs/sdks/agent-analytics/sdk)**

That page covers installation, the full event taxonomy, sessions, tools, spans, scores, multi-agent delegation, streaming, edge runtimes, provider-specific notes (OpenAI, AzureOpenAI, Anthropic, Gemini, Bedrock, Mistral, Anthropic Managed Agents), cost and prompt-cache handling, privacy modes, content shaping, verification with `MockAmplitudeAI`, and the complete API.

<a id="install"></a>
## Install

```bash
pip install amplitude-ai
```

<a id="quick-start"></a>
## Quick start

```python
import os
from amplitude_ai import AmplitudeAI
from amplitude_ai.providers.openai import OpenAI

ai = AmplitudeAI(api_key=os.environ["AMPLITUDE_AI_API_KEY"])
openai = OpenAI(amplitude=ai, api_key=os.environ["OPENAI_API_KEY"])
agent = ai.agent("my-agent")

async def handle_chat(user_id: str, session_id: str, messages: list):
    session = agent.session(user_id=user_id, session_id=session_id)
    with session.run() as s:
        s.track_user_message(messages[-1]["content"])
        response = await openai.chat.completions.create(model="gpt-4o", messages=messages)
        return response.choices[0].message.content
    ai.flush()
```

Emits `[Agent] User Message`, `[Agent] AI Response`, and `[Agent] Session End` — tied to `user_id` and `session_id`. Auto-captures model, tokens, cost, and latency on the AI Response. Read the [SDK reference](https://amplitude.com/docs/sdks/agent-analytics/sdk) for the rest.

<a id="provider-wrappers"></a>
## Provider wrappers

Each wrapper records request, response, tokens, latency, and cost automatically:

```python
from amplitude_ai.providers import openai, anthropic, azure_openai, gemini, bedrock, mistral
```

See [Auto-instrument provider calls](https://amplitude.com/docs/sdks/agent-analytics/sdk#auto-instrument-provider-calls) for the full table and per-provider notes (Anthropic Managed Agents).

**Provider wrappers + delegation:** Inside `session.run_as()` / `session.arun_as()`, provider wrappers suppress auto `[Agent] User Message` emission so that internal `role: "user"` prompts in delegation calls don't create spurious user turns.

<a id="works-with"></a>
## Works with

OpenAI-compatible gateways and OTEL partners — no special SDK build required:

| Partner | Path | Notes |
| --- | --- | --- |
| **Fireworks AI** | SDK-through (`base_url`) or OTLP | Python (`amplitude-ai`) and Node (`@amplitude/ai`) use the OpenAI-compatible client. The SDK detects `*.fireworks.ai`, labels the provider `fireworks`, and captures `response.id` as `[Agent] Provider Request ID`. |
| **OpenRouter** | SDK-through (`base_url`) | Pass the **canonical** routed model id (e.g. `gpt-4o-mini`), not `openrouter/auto`. Respect OpenRouter Privacy Mode for content. |
| **LiteLLM** | SDK-through or OTLP | Set `CAPTURE_MESSAGE_CONTENT=true` when you need message bodies on OTEL spans. |
| **Requesty** | SDK-through only | No OTLP push today — point the OpenAI client at Requesty's base URL. |
| **Strands → Nova** | OTLP | `StrandsTelemetry` → Amplitude Nova / `enable_otel()` mapper. |
| **Google ADK** | OTLP | google-adk 2.13.0 exports native OTLP/HTTP. [Guide](https://amplitude.com/docs/amplitude-ai/agent-analytics/integrations/google-adk). No SDK runtime code. |
| **Pydantic AI** | OTLP | pydantic-ai 2.63.0 exports native OTLP/HTTP. [Guide](https://amplitude.com/docs/amplitude-ai/agent-analytics/integrations/pydantic-ai). No SDK runtime code. |
| **Microsoft Agent Framework** | OTLP | agent-framework-core 1.23.0 exports native OTLP/HTTP. [Guide](https://amplitude.com/docs/amplitude-ai/agent-analytics/integrations/microsoft-agent-framework). No SDK runtime code. |
| **OpenInference** | OTLP | Agno 3.1.11, smolagents 1.26.0, DSPy 3.4.1, Haystack 3.4.1, google-genai 2.33.0, and AutoGen AgentChat 0.7.5. [Guide](https://amplitude.com/docs/amplitude-ai/agent-analytics/integrations/openinference). No SDK runtime code. |

For OTLP partners, emit at least these three GenAI attributes so cost and model fill rates work: `gen_ai.request.model`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`.

Tag gateway traffic with `context={"ingestion_path": "gateway", "gateway": "openrouter"|"litellm"|"requesty"}` (existing `[Agent] Context` — no new schema fields). Full recipes: the "Works with" section of `amplitude-ai.md`, the instrumentation guide bundled with the package.

<a id="fireworks-ai-openai-compatible"></a>
### Fireworks AI (OpenAI-compatible)

Use the existing OpenAI-compatible wrapper; there is no separate
`providers/fireworks.py`:

```python
from amplitude_ai import wrap
from openai import OpenAI

client = wrap(
    OpenAI(
        api_key=os.environ["FIREWORKS_API_KEY"],
        base_url="https://api.fireworks.ai/inference/v1",
    ),
    amplitude=ai,
)
```

Within an Agent Analytics session, Chat Completions and Responses API calls
capture the provider-owned response ID on the assistant event. Path B remains
Fireworks-managed OTLP using `gen_ai.response.id`; it does not require the AI
SDK.

Cost for Fireworks routers: a tier router such as
`accounts/fireworks/routers/kimi-k3-fast` is priced at its tier rate. Other
routers, FireRouter included, are priced at the model the response reports it
selected. If that model is not a Fireworks model (a bring-your-own-key Claude
call, for example), the SDK prices it by model name, and omits
`[Agent] Cost USD` when nothing matches rather than recording `$0`.

<a id="fan-out-llm-parallel-child-calls"></a>
## Fan-out LLM (parallel child calls)

When a single user action triggers multiple parallel LLM calls, use `arun_as()` to delegate:

```python
async with orchestrator.session(session_id=sid) as s:
    s.new_trace()
    s.track_user_message("Generate plan from quiz results", context=state)

    async def run_child(child):
        async with s.arun_as(child) as _cs:
            return await client.chat.completions.create(model="gpt-4o", messages=[...])

    a, b = await asyncio.gather(run_child(scorer), run_child(matcher))
    s.track_ai_message(content=assemble(a, b), model="gpt-4o", provider="openai")
```

This ensures one trace, one user turn, one AI response — regardless of how many internal LLM calls are dispatched. See Step 3g-b in `amplitude-ai.md`, the instrumentation guide bundled with the package, for details.

<a id="auto-instrument-with-an-ai-coding-agent"></a>
## Auto-instrument with an AI coding agent

```bash
amplitude-ai
```

Paste the printed prompt into Cursor, Claude Code, GitHub Copilot, or Codex. The agent reads the instrumentation guide bundled with the package (`amplitude-ai.md`), scans your project, discovers your LLM call sites, and instruments everything — provider wrappers, sessions, multi-agent delegation, tools, scoring, and a verification test.

<a id="privacy"></a>
## Privacy

Three content modes control what reaches Amplitude:

- `full` (default) — content captured, PII redacted by default.

> **Changed in 1.17.0:** `AIConfig.redact_pii` now defaults to `True`, matching the provider wrappers, `patch()`, and the Node SDK. Built-in redaction scrubs emails, phone numbers, SSNs, credit card numbers, and IP addresses before events leave your process. To keep raw content, pass `AIConfig(redact_pii=False)`.
- `metadata_only` — token counts, latency, model, cost. No content.
- `customer_enriched` — no content; you provide structured summaries via `track_session_enrichment()`.

Full details and per-provider redaction recipes at [Choose a privacy mode](https://amplitude.com/docs/sdks/agent-analytics/sdk#choose-a-privacy-mode).

Structured content channels, including tool input/output, attachments, and span state, recognize complete JSON object/array strings up to 100,000 characters, within the runtime parser limits. Built-in PII redaction applies to decoded string values and object keys. Numeric values under common phone, mobile, card/PAN, and SSN field names become quoted redaction placeholders, including values in nested containers and serialized JSON strings. Field matching ignores case and punctuation (for example, `phone_number`, `creditCardNumber`, and `social-security-number`). Other numeric tokens, including timestamps and large integers, retain their exact spelling. If built-in redaction would merge distinct object keys, the serialized content becomes `[content redacted]` instead of losing a field. This correction applies to serialized JSON; native object keys and numeric values retain their existing behavior. Unlabelled numbers and unrecognized field names cannot reliably identify PII: sanitize sensitive source fields or use a custom redactor. Custom patterns and callbacks retain their original string boundary and run once after built-in redaction, followed by base64 redaction. If these later steps make an originally valid JSON document invalid, the SDK emits `[content redacted]`. Native parser limits or excessive nested JSON-string depth also fail closed. Numeric-looking strings, including timestamps embedded in HTML source, remain ordinary redaction. Malformed or oversized JSON follows the ordinary text-redaction and truncation path; existing final size limits can still truncate output.

<a id="html-artifacts"></a>
## HTML artifacts

Artifact envelopes use existing tool, attachment, and span content fields; use the installed SDK version's tracking APIs, with no artifact-specific preparation API. Producers own HTML privacy and fidelity. Existing string redaction is best effort and does not inspect rendered text, entities, or CSS. Content modes still apply, and the viewer remains responsible for safe rendering.

Preflight the complete serialized field, including the envelope and siblings, before emission. Existing SDK truncation can break JSON or HTML; compact the representation only if all meaningful content is preserved; otherwise report the oversize condition and omit the optional artifact at the producer while retaining the underlying event. See **HTML artifacts** in the bundled guide (`amplitude-ai --print-guide`) for details.

<a id="otel-span-first-mode"></a>
## OTEL Span-First Mode

Route all instrumentation through OpenTelemetry spans. The SDK's `AmplitudeEventSpanProcessor` maps GenAI semantic convention spans into `[Agent]` events. Provider wrappers and decorators continue to work — de-duplication prevents double-counting.

```python
ai = AmplitudeAI(api_key=os.environ["AMPLITUDE_AI_API_KEY"])
ai.enable_otel(default_user_id="fallback-uid")
```

Requires optional deps: `pip install opentelemetry-api opentelemetry-sdk`

By default only `gen_ai.*` and `amplitude.*` spans are mapped, and span content follows your `content_mode`. Pass `otel_span_filter="all"` to map every span.

Use `@observe(type="tool"|"agent"|"llm"|"span")` to create typed OTEL spans that map to the corresponding `[Agent]` event types. Use `ai.using_attributes()` to propagate context to spans within a block.

<a id="review-outbound-traffic"></a>
## Review outbound traffic

`AIConfig(review_mode=True)` records each post-redaction event and prints a sanitized upload preview without sending it. Previews are labeled `pre-transport`: they show the event handed to the analytics client, not gzip, retries, or fields the analytics SDK adds later. The envelope API key is `[REDACTED]`. Prompt and message content is left intact — do not commit the artifact.

```python
import os
from amplitude_ai import AIConfig, AmplitudeAI

ai = AmplitudeAI(
    api_key=os.environ["AMPLITUDE_AI_API_KEY"],
    config=AIConfig(review_mode=True, validate=True),
)
# Exercise one representative session, then:
ai.flush()
print(ai.get_review_traffic())
```

`patch(amplitude, review_mode=True)` covers the raw-client instrumentation path that does not go through `AmplitudeAI`. `AMPLITUDE_AI_REVIEW=true` enables that path. Read those previews with `get_patch_review_traffic()`. `unpatch()` restores `track` and keeps the captured previews until `clear_patch_review_traffic()`.

Confirm in the network panel that no Amplitude upload left the process. `AMPLITUDE_AI_DEBUG` does not enable review mode.

<a id="local-verification"></a>
## Local Verification

`MockAmplitudeAI.summary()` prints a fill-rate report showing which of the key Agent Analytics fields (identity, session, model, provider, latency, tokens, cost) are populated across your captured events:

```python
from amplitude_ai.testing import MockAmplitudeAI

mock = MockAmplitudeAI()
agent = mock.agent("test-agent")
with agent.session(user_id="u1", session_id="s1") as s:
    s.track_user_message("hello")
    s.track_ai_message("response", "gpt-4o-mini", "openai", 150)

print(mock.summary())
```

<a id="experiments-record-which-variant-an-agent-ran-under"></a>
## Experiments: record which variant an agent ran under

Pass the assignment as `experiments` on the agent, the session, or a single agent or session `track_*` call. Every `[Agent]` event then carries it, so Agent Analytics can compare quality, cost, latency and outcomes by arm, whichever tool assigned the variant:

```python
with agent.session(user_id="u1", experiments=[{"key": "planner", "variant": "v2", "provider": "statsig"}]) as s:
    ...
```

Every event in the session then carries `[Agent] Experiments` (`["planner:v2"]`), an array with one `key:variant` string per experiment: split any chart by it to compare arms, or filter on one value. When exactly one experiment applies the event also carries `[Agent] Experiment Key`, `Variant`, `Unit`, and `Provider` as plain properties. `unit` says what was randomized: `"agent_session"` (the default), `"turn"`, or `"span"`. A session entry overrides an agent entry with the same key, and a call entry overrides both. Child agents (`agent.child()`, `session.run_as()`) inherit the experiments. At most 32 apply to an event; invalid entries are dropped with a debug log, never raised. Earlier code that put `experiment_variant` in `context` keeps working, but use `experiments` for new code so the assignment isn't recorded twice. For model or prompt tests you can also set `agent_version` on the agent or `prompt_id` on AI responses.

Take the assignment from the flag tool the app already uses instead of typing it out:

```python
from amplitude_ai import experiments_from_amplitude

variants = experiment_client.fetch_v2(User(user_id="u1"))        # Amplitude Experiment
with agent.session(user_id="u1", experiments=experiments_from_amplitude(variants, keys=["planner"])) as s:
    ...
```

```python
from openfeature import api
from amplitude_ai.openfeature import AmplitudeAIHook

api.add_hooks([AmplitudeAIHook(flags=["planner"])])                # LaunchDarkly, Statsig, GrowthBook, ... via OpenFeature
with agent.session(user_id="u1") as s:
    planner = api.get_client().get_string_value("planner", "v1", ctx)   # recorded as planner:<variant>
```

Name the experiment flags with `keys=` / `flags=`: a project's flag result also holds configuration flags that are not experiments, and an event carries at most 32 entries. Without `keys=`, `experiments_from_amplitude` records every non-fallback flag and warns when more than 32 would not fit. It sets `provider: "amplitude"`. The OpenFeature hook records every flag evaluated inside a session with the provider's name lowercased (a `provider=` you pass is stored as given); pass `unit="turn"` when flags are evaluated per turn, `include_defaults=True` to keep evaluations that returned the caller's default, and `variant_of=lambda key, value: ...` to name the arm for object-valued flags whose provider sets no variant (Statsig experiments and dynamic configs return their parameters). An assignment learned after the session was entered can be recorded with `s.add_experiments([...])` or the module-level `amplitude_ai.add_experiments([...])`; events tracked from then on carry it, and a session shares one list with its `run_as()` children. Neither helper sends an exposure event: your flag tool keeps doing that.

<a id="voice-agents"></a>
## Voice agents

Track a call as one session (call ID as `session_id`) and each utterance as a turn, passing `voice_turn_properties(...)` as `event_properties`. Besides speech duration, time to first audio, and barge-in, it takes stage latency (`stt_ms`, `llm_ttft_ms`, `tts_ttfb_ms`), the turn's place on the call timeline (`turn_start_offset_ms`, `turn_end_offset_ms`), `pre_reply_silence_ms`, and `overlap_ms`. Omit what the platform doesn't report; absent means unknown.

```python
from amplitude_ai.integrations.livekit import attach
from amplitude_ai.integrations.pipecat import PipecatVoiceTracker, amplitude_observer

attach(lk_session, agent, model="gpt-4o-mini", provider="openai")                        # LiveKit Agents
task = PipelineTask(pipeline, observers=[amplitude_observer(PipecatVoiceTracker(agent))])  # Pipecat
```

Neither integration imports its framework at load. `RealtimeTurnMapper` (OpenAI Realtime) and `NovaSonicTurnMapper` (Amazon Nova Sonic) map raw events. Enrichment rolls the turns up on `[Agent] Session Record`: stage averages, call duration, and threshold-free call measures such as `[Agent] Voice Max Pre Reply Silence Ms`, `[Agent] Voice First Reply Ms`, and `[Agent] Voice Ended Mid Reply`. Pick your own thresholds when you filter or chart them.

<a id="coding-agents"></a>
## Coding agents

Send the sessions of the AI coding agents that work on your repo (Claude Code, Codex CLI, goose) to Agent Analytics, and tie their commits to merges, reverts and deploys:

```bash
export AMPLITUDE_AI_API_KEY=...         # read from the environment, never printed
amplitude-ai coding-agents init --dry-run
amplitude-ai coding-agents init         # --harness claude-code|codex|goose, --privacy metadata, --region eu
amplitude-ai coding-agents doctor
```

`init` writes a `prepare-commit-msg` hook (adds `Agent-Session-Id` / `Agent-Name` trailers, plus `Agent-Trigger`, the same value as `[Agent] Session Trigger`), a GitHub workflow (`Commit Merged`, `Commit Reverted`, `Pull Request Merged`, `Deploy Succeeded`, `Deploy Failed`, each with `agent_trigger` and `agent_setup_version`, a hash of the agent setup files listed in `AMPLITUDE_AGENT_SETUP_PATHS`; `Pull Request Merged` adds `hours_open`, `hours_to_first_review` and `review_rounds`), Claude Code telemetry settings, and the Codex Stop hook `amplitude-ai hook codex`, and prints goose's OTLP environment. The key only goes to gitignored local files. Tool Calls that create a commit carry `commit_sha`. For apps that drive Codex through the `openai-codex` SDK, use `amplitude_ai.integrations.codex_sdk.CodexSDKTracker`. For agents in separate processes, `amplitude_ai.propagation` carries session context in A2A message metadata (`inject_a2a_metadata` / `extract_a2a_metadata`) and ACP `_meta` (`inject_acp_meta` / `extract_acp_meta`).

<a id="agents-in-other-processes"></a>
## Agents in other processes

When one agent calls another over A2A, ACP or HTTP, the caller sends its agent context and the receiver passes the whole extracted result to `session(context=...)`:

```python
from amplitude_ai.propagation import extract_a2a_metadata, inject_a2a_metadata

message.metadata.update(inject_a2a_metadata())          # caller, inside its session

ctx = extract_a2a_metadata(request.message.metadata)    # receiver
with agent.session(context=ctx) as s:
    ...
```

Set `AMPLITUDE_AI_CONTEXT_KEYS='{"k1": "<secret of 32+ bytes>"}'` on both sides to sign the context. A context whose signature verifies continues the caller's session, trace and parent agent; the signed context carries the caller's exact trace ID, so both sides record the same `[Agent] Trace ID`. Anything else starts a fresh session and links the caller through `[Agent] Peer Agent ID`, `[Agent] Peer Session ID` and `[Agent] Peer Trace ID`. Every event also carries `[Agent] Context Trust`, `[Agent] Context Verify Result`, `[Agent] Actor Type` and `[Agent] Identity Type`. No user ID is sent unless you pass `identity="pairwise"` (a per-recipient pseudonym) or `identity="raw"` (signed contexts only).

Link one tool call to the agent that answered it with `track_tool_call(..., peer=extract_a2a_metadata(reply.metadata))`; the receiver adds its context to the reply with `inject_a2a_response_metadata()`. Put `ai.context_properties()` on business events, such as a checkout, to join them to the agent session without sending a user ID. FastAPI and Starlette apps can pass `accept_context=True` to `AmplitudeAIMiddleware`. A receiver without signing keys still accepts unsigned HTTP headers for now and logs a one-time deprecation warning; a receiver with keys treats them as untrusted. The Node SDK (`@amplitude/ai`) uses the same envelope. The full guide is Step 3j in `amplitude-ai.md`.

<a id="event-property-reference"></a>
## Event Property Reference

<a id="event-timing-contract"></a>
### Event Timing Contract

Two rules apply to every `[Agent] *` event and every property listed below —
read them before interpreting `event_time` or `[Agent] Latency Ms`:

1. **`event_time` is the emit instant = operation END.** The SDK stamps
   `event_time` at the moment the operation finishes (LLM response received,
   tool call returned, session closed). It is not when the operation started.
2. **`[Agent] Latency Ms` is measured backward from `event_time`.** It is the
   wall-clock duration of the operation and represents `event_time - operation_start`.
   Ingest consumers that need a derived span time therefore compute
   `span.end_time = event_time` and `span.start_time = event_time - latency_ms`.

**Streaming.** For streaming responses, the SDK emits `[Agent] AI Response`
at stream finalize (last token received). `[Agent] Latency Ms` covers the
full stream duration. `[Agent] TTFB Ms` covers only the time to the first
token — measure perceived responsiveness with TTFB, and total generation
time with `Latency Ms`.

**Latency-less events.** `[Agent] User Message`, `[Agent] Session End`, and
`[Agent] Score` do not carry `[Agent] Latency Ms`. Ingest treats these as
point-in-time (`span.start_time == span.end_time == event_time`).

<!-- BEGIN EVENT PROPERTY REFERENCE -->

<a id="common-properties-present-on-all-sdk-events"></a>
### Common Properties (present on all SDK events)

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
| `[Agent] Trace ID` | string | No | Groups events into a single "Turn" in the trace viewer. All events in one user-to-AI-response exchange share the same trace ID. Each distinct trace ID becomes a collapsible turn card. Events without a trace ID fall back to the session ID and collapse into Turn 1. |
| `[Agent] Turn ID` | number | No | Monotonically increasing counter for event ordering within a session. |
| `[Agent] Agent ID` | string | No | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
| `[Agent] Parent Agent ID` | string | No | For multi-agent orchestration: the agent that delegated to this agent. |
| `[Agent] Peer Agent ID` | string | No | Agent context: the other agent this event is linked to without trusting it. On a session's events, the caller whose context was missing a valid signature, so this session started fresh. On an [Agent] Tool Call, the agent that answered that call. |
| `[Agent] Peer Session ID` | string | No | Agent context: the session ID of the linked peer agent (see [Agent] Peer Agent ID). Join it to that agent's [Agent] Session ID to follow a call across agents that do not share a signing key. |
| `[Agent] Peer Trace ID` | string | No | Agent context: the trace ID an untrusted caller sent. The session keeps its own [Agent] Trace ID and records the caller's here. |
| `[Agent] Context Trust` | string | No | Agent context: how far the session trusted the caller's context. 'trusted' (signature verified: session, user, trace and parent agent were continued), 'untrusted' (context present but not verified: the session started fresh and the caller is a peer) or 'none' (no context was sent). |
| `[Agent] Context Verify Result` | string | No | Agent context: the signature check result for the caller's context, or for a tool call's response context. One of 'ok', 'none', 'unsigned', 'malformed', 'unsupported_alg', 'unknown_kid', 'bad_signature', 'expired', 'not_yet_valid'. |
| `[Agent] Actor Type` | string | No | Agent context: who sent the request this session handles, 'agent' or 'human'. Agent-to-agent protocol requests are always 'agent'; for other transports the signed value is used when the context verifies, otherwise 'human'. |
| `[Agent] Identity Type` | string | No | Agent context: what kind of user ID the caller shared. 'account' (the caller's own user ID), 'pairwise' (a per-recipient pseudonym, stable for one user and one recipient) or 'none'. |
| `[Agent] Customer Org ID` | string | No | Organization ID for multi-tenant platforms. Enables account-level group analytics. |
| `[Agent] Agent Version` | string | No | Agent code version (e.g., 'v4.2'). Enables version-over-version quality comparison. |
| `[Agent] Agent Description` | string | No | Human-readable description of the agent's purpose (e.g., 'Handles user chat requests via OpenAI GPT-4o'). Enables observability-driven agent registry from event streams. |
| `[Agent] Context` | string | No | Serialized JSON dict of arbitrary segmentation dimensions (surface, prompt_revision, etc.). Experiment assignment has its own [Agent] Experiment properties. |
| `[Agent] Env` | string | No | Deployment environment: 'production', 'staging', or 'dev'. |
| `[Agent] SDK Version` | string | Yes | Version of the amplitude-ai SDK that produced this event. |
| `[Agent] Runtime` | string | Yes | SDK runtime: 'python' or 'node'. |
| `[Agent] Content Mode` | string | No | Privacy posture the caller configured: 'full', 'metadata_only', or 'customer_enriched'. A metadata channel, so it is present even on the events whose content it suppresses -- which is what lets a contentless session be read as a deliberate choice rather than as an agent that answered nothing. Absent on events from SDK versions predating the property. |
| `[Agent] Tags` | string | No | Serialized JSON dict of session-level tags for segmentation (e.g., experiment, surface, feature_flag). |
| `[Agent] Experiments` | string[] | No | Experiments this event ran under, one 'key:variant' string each (at most 32). Set by the SDK experiments option on agent(), session() or a single call, or from OTel feature_flag evaluations. Sent whenever at least one experiment applies; split any chart by it to compare arms. |
| `[Agent] Experiment Key` | string | No | Experiment or flag key. Sent only when exactly one experiment applies to the event. |
| `[Agent] Experiment Variant` | string | No | Variant this event ran under. Sent only when exactly one experiment applies; [Agent] Experiments carries every assignment. |
| `[Agent] Experiment Unit` | string | No | What the experiment randomized: 'agent_session' (default), 'turn' or 'span'. Sent only when exactly one experiment applies. |
| `[Agent] Experiment Provider` | string | No | Experimentation tool that assigned the variant, such as 'amplitude', 'statsig' or 'launchdarkly'. Sent only when exactly one experiment applies and the provider is set. |
| `[Agent] Git SHA` | string | No | Git commit SHA of the deployed agent code. Auto-captured from environment or git CLI. |
| `[Agent] Git Ref` | string | No | Git branch or tag name (e.g., 'main', 'v1.2.3'). Auto-captured from environment or git CLI. |
| `[Agent] Git Repo` | string | No | Git repository URL. Auto-captured from environment or git CLI. |

<a id="user-message-properties"></a>
### User Message Properties

Event-specific properties for `[Agent] User Message` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Message ID` | string | Yes | Unique identifier for this message event (UUID). Used to link scores and tool calls back to specific messages. |
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
| `[Agent] Locale` | string | No | User locale (e.g., 'en-US'). |
| `[Amplitude] Session Replay ID` | string | No | Links to Amplitude Session Replay (format: device_id/session_id). Enables one-click navigation from AI session to browser replay. |
| `[Agent] Is Regeneration` | boolean | No | Whether the user requested the AI regenerate a previous response. |
| `[Agent] Is Edit` | boolean | No | Whether the user edited a previous message and resubmitted. |
| `[Agent] Edited Message ID` | string | No | The message_id of the original message that was edited (links the edit to the original). |
| `[Agent] Has Attachments` | boolean | No | Whether this message includes file attachments (uploads, images, etc.). |
| `[Agent] Attachment Types` | string[] | No | Distinct attachment types (e.g., 'pdf', 'image', 'csv'). Serialized JSON array. |
| `[Agent] Attachment Count` | number | No | Number of file attachments included with this message. |
| `[Agent] Total Attachment Size Bytes` | number | No | Total size of all attachments in bytes. |
| `[Agent] Attachments` | string | No | Serialized JSON array of attachment metadata (type, name, size_bytes, mime_type). Only metadata, never file content. |
| `[Agent] Channel` | string | No | Channel the turn happened on: 'chat' or 'voice'. Omitted means chat. A session is voice if any of its turns is voice. |
| `[Agent] Voice Direction` | string | No | For voice turns: 'inbound' or 'outbound' phone call, or 'in_app' for an assistant inside the product. Omitted when unknown. |
| `[Agent] Voice Speech Duration Ms` | number | No | Milliseconds of speech in this turn. Omitted when the platform does not report it. |
| `[Agent] Voice Time To First Audio Ms` | number | No | Milliseconds from the end of the caller's turn until the agent's first audible audio. This is what the caller hears. TTFB Ms is token latency and is a different number. |
| `[Agent] Voice Interrupted` | boolean | No | Whether this turn was spoken over by the other side. Omitted when the platform does not report it. |
| `[Agent] Voice Interrupted By` | string | No | Who spoke over whom: 'user' or 'agent'. Set only when the platform reports the side. |
| `[Agent] Voice Yielded` | boolean | No | On an agent turn: true if the response was cut off because the caller spoke; false if the caller spoke and the response finished anyway. Omitted when the platform does not report it. |
| `[Agent] Voice End Of Turn Reason` | string | No | Why the turn ended: completed, interrupted, silence, transferred, or error. The platform's raw reason is in [Agent] Context. |
| `[Agent] Voice Transcript Confidence` | number | No | Recognizer confidence for the transcript, from 0 to 1. Omitted when the recognizer does not return one. |
| `[Agent] Voice STT Ms` | number | No | Speech-to-text latency: milliseconds from the end of the caller's speech to the final transcript. Reported on the agent turn it delayed. Omitted when the platform does not report it. |
| `[Agent] Voice LLM TTFT Ms` | number | No | Milliseconds until the model's first token for this agent turn. Omitted when the platform does not report it. |
| `[Agent] Voice TTS TTFB Ms` | number | No | Milliseconds until the speech synthesizer's first audio byte for this agent turn. Omitted when the platform does not report it. |
| `[Agent] Voice Turn Start Offset Ms` | number | No | When this turn's speech started, in milliseconds from the start of the call. Omitted when the platform does not report it. |
| `[Agent] Voice Turn End Offset Ms` | number | No | When this turn's speech ended, in milliseconds from the start of the call. Never before the start offset. Omitted when the platform does not report it. |
| `[Agent] Voice Pre Reply Silence Ms` | number | No | On an agent turn: milliseconds of silence the caller heard before the agent started replying. Omitted when the platform does not report it. |
| `[Agent] Voice Overlap Ms` | number | No | Milliseconds this turn's speech overlapped the other side's previous turn. Set on the turn that started while the other side was still speaking. Omitted when the platform does not report it. |
| `[Agent] Message Labels` | string | No | Serialized JSON array of MessageLabel objects (key-value pairs with optional confidence). Used for routing tags, classifier output, business context. |
| `[Agent] Message Source` | string | No | Origin of the user message: 'user' for real end-user input, 'agent' for inter-agent delegation (parent agent sending instructions to a child agent). Automatically set by provider wrappers based on parent_agent_id context. An agent context that is not verified never sets the parent agent (except unsigned HTTP headers while those are still accepted), so those messages stay 'user'. |
| `[Agent] Session Trigger` | string | No | How the session was started: interactive (a person at the keyboard), headless (a non-interactive run such as codex exec), ci (a CI job), scheduled (a schedule), or event (a webhook or channel event). Unset when the harness can't tell. Delegation is not a trigger; see [Agent] Agent Chain Depth. |
| `[Agent] Root Agent Name` | string | No | Entry-point agent in multi-agent flows. |
| `[Agent] Agent Chain Depth` | number | No | The emitting agent's depth in the delegation tree. The root agent is 1, and each delegation adds 1. |

<a id="ai-response-properties"></a>
### AI Response Properties

Event-specific properties for `[Agent] AI Response` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Message ID` | string | Yes | Unique identifier for this message event (UUID). Used to link scores and tool calls back to specific messages. |
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
| `[Agent] Model Name` | string | Yes | LLM model identifier (e.g., 'gpt-4o', 'claude-sonnet-4-20250514'). |
| `[Agent] Provider` | string | Yes | LLM provider name (e.g., 'openai', 'anthropic', 'google', 'mistral', 'bedrock'). |
| `[Agent] Provider Request ID` | string | No | Provider-owned inference identifier. On AI Response this is captured from the provider response; on Score it is present only when explicitly supplied. |
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
| `[Agent] Is Error` | boolean | Yes | Whether this event represents an error condition. |
| `[Agent] Error Message` | string | No | Error message text when Is Error is true. |
| `[Agent] Error Type` | string | No | Exception class name when Is Error is true (e.g., 'ValidationError', 'TimeoutError'). Distinct from Error Message so downstream tooling can group failures by type without parsing the message. |
| `[Agent] Stack Trace` | string | No | Stack trace captured on error when capture_stack_trace=True in AIConfig. Privacy-gated. |
| `[Agent] Error Source` | string | No | Origin of the error: 'provider' (LLM API), 'tool' (tool execution), 'framework' (SDK/pipeline), or 'user' (validation). |
| `[Agent] Locale` | string | No | User locale (e.g., 'en-US'). |
| `[Agent] Span Kind` | string | No | Classification of the span type for OTEL bridge compatibility. |
| `[Amplitude] Session Replay ID` | string | No | Links to Amplitude Session Replay (format: device_id/session_id). Enables one-click navigation from AI session to browser replay. |
| `[Agent] TTFB Ms` | number | No | Time to first byte/token in milliseconds. Measures perceived responsiveness for streaming. |
| `[Agent] Channel` | string | No | Channel the turn happened on: 'chat' or 'voice'. Omitted means chat. A session is voice if any of its turns is voice. |
| `[Agent] Voice Direction` | string | No | For voice turns: 'inbound' or 'outbound' phone call, or 'in_app' for an assistant inside the product. Omitted when unknown. |
| `[Agent] Voice Speech Duration Ms` | number | No | Milliseconds of speech in this turn. Omitted when the platform does not report it. |
| `[Agent] Voice Time To First Audio Ms` | number | No | Milliseconds from the end of the caller's turn until the agent's first audible audio. This is what the caller hears. TTFB Ms is token latency and is a different number. |
| `[Agent] Voice Interrupted` | boolean | No | Whether this turn was spoken over by the other side. Omitted when the platform does not report it. |
| `[Agent] Voice Interrupted By` | string | No | Who spoke over whom: 'user' or 'agent'. Set only when the platform reports the side. |
| `[Agent] Voice Yielded` | boolean | No | On an agent turn: true if the response was cut off because the caller spoke; false if the caller spoke and the response finished anyway. Omitted when the platform does not report it. |
| `[Agent] Voice End Of Turn Reason` | string | No | Why the turn ended: completed, interrupted, silence, transferred, or error. The platform's raw reason is in [Agent] Context. |
| `[Agent] Voice Transcript Confidence` | number | No | Recognizer confidence for the transcript, from 0 to 1. Omitted when the recognizer does not return one. |
| `[Agent] Voice STT Ms` | number | No | Speech-to-text latency: milliseconds from the end of the caller's speech to the final transcript. Reported on the agent turn it delayed. Omitted when the platform does not report it. |
| `[Agent] Voice LLM TTFT Ms` | number | No | Milliseconds until the model's first token for this agent turn. Omitted when the platform does not report it. |
| `[Agent] Voice TTS TTFB Ms` | number | No | Milliseconds until the speech synthesizer's first audio byte for this agent turn. Omitted when the platform does not report it. |
| `[Agent] Voice Turn Start Offset Ms` | number | No | When this turn's speech started, in milliseconds from the start of the call. Omitted when the platform does not report it. |
| `[Agent] Voice Turn End Offset Ms` | number | No | When this turn's speech ended, in milliseconds from the start of the call. Never before the start offset. Omitted when the platform does not report it. |
| `[Agent] Voice Pre Reply Silence Ms` | number | No | On an agent turn: milliseconds of silence the caller heard before the agent started replying. Omitted when the platform does not report it. |
| `[Agent] Voice Overlap Ms` | number | No | Milliseconds this turn's speech overlapped the other side's previous turn. Set on the turn that started while the other side was still speaking. Omitted when the platform does not report it. |
| `[Agent] Input Tokens` | number | No | Number of input/prompt tokens consumed by this LLM call. |
| `[Agent] Output Tokens` | number | No | Number of output/completion tokens generated by this LLM call. |
| `[Agent] Total Tokens` | number | No | Total tokens consumed (input + output). |
| `[Agent] Reasoning Tokens` | number | No | Tokens consumed by reasoning/thinking (o1, o3, extended thinking models). |
| `[Agent] Cache Read Tokens` | number | No | Input tokens served from the provider's prompt cache (cheaper rate). Used for cache-aware cost calculation. |
| `[Agent] Cache Creation Tokens` | number | No | Input tokens that created new prompt cache entries. |
| `[Agent] Cost USD` | number | No | Estimated cost in USD for this LLM call. Cache-aware when cache token counts are provided. |
| `[Agent] Finish Reason` | string | No | Why the model stopped generating: 'stop', 'end_turn', 'tool_use', 'length', 'content_filter', etc. |
| `[Agent] Tool Calls` | string | No | Serialized JSON array of tool call requests made by the AI in this response. |
| `[Agent] Has Reasoning` | boolean | No | Whether the AI response included reasoning/thinking content. |
| `[Agent] Reasoning Content` | string | No | The AI's reasoning/thinking content (when available and content_mode permits). |
| `[Agent] System Prompt` | string | No | The system prompt used for this LLM call (when content_mode permits). Chunked for long prompts. |
| `[Agent] System Prompt Length` | number | No | Character length of the system prompt. |
| `[Agent] Tool Definitions` | string | No | Normalized JSON array of tool definitions sent to the LLM (when content_mode permits). Each entry contains name, description, and parameters schema. |
| `[Agent] Tool Definitions Count` | number | No | Number of tool definitions in the LLM request. |
| `[Agent] Tool Definitions Hash` | string | No | Stable SHA-256 hash of the normalized tool definitions. Always present regardless of content_mode; enables toolset change detection without exposing schemas. |
| `[Agent] Temperature` | number | No | Temperature parameter used for this LLM call. |
| `[Agent] Max Output Tokens` | number | No | Maximum output tokens configured for this LLM call. |
| `[Agent] Top P` | number | No | Top-p (nucleus sampling) parameter used for this LLM call. |
| `[Agent] Is Streaming` | boolean | No | Whether this response was generated via streaming. |
| `[Agent] Prompt ID` | string | No | Identifier for the prompt template or version used. |
| `[Agent] Was Copied` | boolean | No | Whether the user copied this AI response content. An implicit positive quality signal. |
| `[Agent] Was Cached` | boolean | No | Whether this response was served from a semantic/full-response cache (distinct from token-level prompt caching). |
| `[Agent] Model Tier` | string | No | Model tier classification: 'fast' (GPT-4o-mini, Haiku, Flash), 'standard' (GPT-4o, Sonnet, Pro), or 'reasoning' (o1, o3, DeepSeek-R1). Auto-inferred from model name. |
| `[Agent] Has Attachments` | boolean | No | Whether this AI response includes generated attachments (images, charts, files). |
| `[Agent] Attachment Types` | string[] | No | Distinct attachment types in this AI response. Serialized JSON array. |
| `[Agent] Attachment Count` | number | No | Number of attachments generated by the AI in this response. |
| `[Agent] Total Attachment Size Bytes` | number | No | Total size of all AI-generated attachments in bytes. |
| `[Agent] Attachments` | string | No | Serialized JSON array of AI-generated attachment metadata. |
| `[Agent] Message Labels` | string | No | Serialized JSON array of MessageLabel objects attached to this AI response. |
| `[Agent] Message Label Map` | string | No | Serialized JSON map of label key to value for quick lookup. |
| `[Agent] Session Trigger` | string | No | How the session was started: interactive (a person at the keyboard), headless (a non-interactive run such as codex exec), ci (a CI job), scheduled (a schedule), or event (a webhook or channel event). Unset when the harness can't tell. Delegation is not a trigger; see [Agent] Agent Chain Depth. |
| `[Agent] Root Agent Name` | string | No | Entry-point agent in multi-agent flows. |
| `[Agent] Agent Chain Depth` | number | No | The emitting agent's depth in the delegation tree. The root agent is 1, and each delegation adds 1. |

<a id="tool-call-properties"></a>
### Tool Call Properties

Event-specific properties for `[Agent] Tool Call` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
| `[Agent] Is Error` | boolean | Yes | Whether this event represents an error condition. |
| `[Agent] Error Message` | string | No | Error message text when Is Error is true. |
| `[Agent] Error Type` | string | No | Exception class name when Is Error is true (e.g., 'ValidationError', 'TimeoutError'). Distinct from Error Message so downstream tooling can group failures by type without parsing the message. |
| `[Agent] Stack Trace` | string | No | Stack trace captured on error when capture_stack_trace=True in AIConfig. Privacy-gated. |
| `[Agent] Error Source` | string | No | Origin of the error: 'provider' (LLM API), 'tool' (tool execution), 'framework' (SDK/pipeline), or 'user' (validation). |
| `[Agent] Locale` | string | No | User locale (e.g., 'en-US'). |
| `[Agent] Span Kind` | string | No | Classification of the span type for OTEL bridge compatibility. |
| `[Amplitude] Session Replay ID` | string | No | Links to Amplitude Session Replay (format: device_id/session_id). Enables one-click navigation from AI session to browser replay. |
| `[Agent] Invocation ID` | string | Yes | Unique identifier for this tool invocation (UUID). Used to link tool calls to parent messages. |
| `[Agent] Tool Name` | string | Yes | Name of the tool/function that was invoked (e.g., 'search_docs', 'web_search'). |
| `[Agent] Tool Type` | string | No | Origin/class of the tool: 'python' (native in-process tool), 'mcp' (tool served by a connected MCP server), 'flow' (sub-agent delegation), or 'unknown'. Lets you segment native vs. MCP-connected tool usage without parsing the tool name or Context. |
| `[Agent] Tool Owner` | string | No | Who owns the tool: 'amplitude' (Amplitude-built/operated — native tools, sub-agents, and Amplitude's own MCP servers) or 'customer' (a server the customer connected, e.g. their own MCP server via the gateway). Segments Amplitude-internal vs. customer-connected tool usage as a first-class property, without overloading [Agent] Tool Type. |
| `[Agent] Tool Success` | boolean | Yes | Whether the tool call completed successfully. |
| `[Agent] Tool Input` | string | No | Serialized JSON of the tool's input arguments. Only sent when content_mode='full'. |
| `[Agent] Tool Output` | string | No | Serialized JSON of the tool's output/return value. Only sent when content_mode='full'. |
| `[Agent] Parent Message ID` | string | No | The message_id of the user message that triggered this tool call. Links the tool call into the event graph. |
| `[Agent] Session Trigger` | string | No | How the session was started: interactive (a person at the keyboard), headless (a non-interactive run such as codex exec), ci (a CI job), scheduled (a schedule), or event (a webhook or channel event). Unset when the harness can't tell. Delegation is not a trigger; see [Agent] Agent Chain Depth. |
| `[Agent] Root Agent Name` | string | No | Entry-point agent in multi-agent flows. |
| `[Agent] Agent Chain Depth` | number | No | The emitting agent's depth in the delegation tree. The root agent is 1, and each delegation adds 1. |

<a id="embedding-properties"></a>
### Embedding Properties

Event-specific properties for `[Agent] Embedding` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
| `[Agent] Model Name` | string | Yes | LLM model identifier (e.g., 'gpt-4o', 'claude-sonnet-4-20250514'). |
| `[Agent] Provider` | string | Yes | LLM provider name (e.g., 'openai', 'anthropic', 'google', 'mistral', 'bedrock'). |
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
| `[Agent] Span ID` | string | Yes | Unique identifier for this embedding operation (UUID). |
| `[Agent] Input Tokens` | number | No | Number of input tokens processed by the embedding model. |
| `[Agent] Embedding Dimensions` | number | No | Dimensionality of the output embedding vector. |
| `[Agent] Cost USD` | number | No | Estimated cost in USD for this embedding operation. |

<a id="span-properties"></a>
### Span Properties

Event-specific properties for `[Agent] Span` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
| `[Agent] Is Error` | boolean | Yes | Whether this event represents an error condition. |
| `[Agent] Error Message` | string | No | Error message text when Is Error is true. |
| `[Agent] Error Type` | string | No | Exception class name when Is Error is true (e.g., 'ValidationError', 'TimeoutError'). Distinct from Error Message so downstream tooling can group failures by type without parsing the message. |
| `[Agent] Stack Trace` | string | No | Stack trace captured on error when capture_stack_trace=True in AIConfig. Privacy-gated. |
| `[Agent] Error Source` | string | No | Origin of the error: 'provider' (LLM API), 'tool' (tool execution), 'framework' (SDK/pipeline), or 'user' (validation). |
| `[Agent] Span ID` | string | Yes | Unique identifier for this span (UUID). |
| `[Agent] Span Name` | string | Yes | Name of the operation (e.g., 'rag_pipeline', 'vector_search', 'rerank'). |
| `[Agent] Parent Span ID` | string | No | Span ID of the parent span for nested pipeline steps. |
| `[Agent] Input State` | string | No | Serialized JSON of the span's input state. Only sent when content_mode='full'. |
| `[Agent] Output State` | string | No | Serialized JSON of the span's output state. Only sent when content_mode='full'. |
| `[Agent] Message ID` | string | No | Stable operation identity for replay deduplication. |
| `[Agent] Cost USD` | number | No | Cost of this operation in USD, separate from model inference. |
| `[Agent] Component Type` | string | No | Operation category, such as cache. |
| `[Agent] Model Name` | string | No | Model associated with this operation. |
| `[Agent] Provider` | string | No | Provider associated with this operation. |
| `cache.name` | string | No | Provider cache resource identifier. |
| `cache.operation` | string | No | Cache operation: create or extend. |
| `cache.tokens` | number | No | Tokens stored in the cache. |
| `cache.storage_seconds` | number | No | Additional lifetime charged by this operation. |
| `cache.storage_start` | string | No | Start of the charged storage interval in UTC. |
| `cache.expire_time` | string | No | End of the charged storage interval in UTC. |
| `cache.creation_cost_usd` | number | No | Input charge for creating the cache; zero on extensions. |
| `cache.storage_cost_usd` | number | No | Storage charge for the additional lifetime. |
| `cache.input_usd_per_million_tokens` | number | No | Applied input rate per million tokens. |
| `cache.storage_usd_per_million_token_hours` | number | No | Applied storage rate per million token-hours. |
| `cache.storage_cost_known` | boolean | No | False when expiration was missing and only creation cost was recorded. |

<a id="session-end-properties"></a>
### Session End Properties

Event-specific properties for `[Agent] Session End` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Enrichments` | string | No | Serialized JSON of SessionEnrichments (topic classifications, rubric scores, outcome, flags). Attached when enrichments are provided at session close. |
| `[Agent] Abandonment Turn` | number | No | Turn ID of the last user message that received an AI response before the user left. Low values (e.g., 1) strongly signal first-response dissatisfaction. |
| `[Agent] Session Idle Timeout Minutes` | number | No | Custom idle timeout for this session (default 30 min). Tells the server how long to wait before auto-closing. |
| `[Agent] Session Trigger` | string | No | How the session was started: interactive (a person at the keyboard), headless (a non-interactive run such as codex exec), ci (a CI job), scheduled (a schedule), or event (a webhook or channel event). Unset when the harness can't tell. Delegation is not a trigger; see [Agent] Agent Chain Depth. |
| `[Agent] Root Agent Name` | string | No | Entry-point agent in multi-agent flows. |
| `[Agent] Agent Chain Depth` | number | No | The emitting agent's depth in the delegation tree. The root agent is 1, and each delegation adds 1. |

<a id="session-enrichment-properties"></a>
### Session Enrichment Properties

Event-specific properties for `[Agent] Session Enrichment` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Enrichments` | string | Yes | Serialized JSON of SessionEnrichments: topic_classifications, rubrics, overall_outcome, quality_score, sentiment_score, boolean flags, agent chain metadata, and message labels. |

<a id="score-properties"></a>
### Score Properties

Event-specific properties for `[Agent] Score` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Score Name` | string | Yes | Name of the score (e.g., 'user-feedback', 'task_completion', 'accuracy', 'groundedness'). |
| `[Agent] Score Value` | number | Yes | Numeric score value. Binary (0/1), continuous (0.0-1.0), or rating scale (1-5). |
| `[Agent] Target ID` | string | Yes | The message_id or session_id being scored. |
| `[Agent] Target Type` | string | Yes | What is being scored: 'message' or 'session'. |
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
| `[Agent] Provider Request ID` | string | No | Provider-owned inference identifier. On AI Response this is captured from the provider response; on Score it is present only when explicitly supplied. |
| `[Agent] Comment` | string | No | Optional text explanation for the score (respects content_mode). |
| `[Agent] Taxonomy Version` | string | No | ID of the evaluation configuration published for the org when this enrichment ran. It changes whenever any evaluator in the org is published, even if this agent's evaluators did not change, so it is not a per-evaluator version: use [Agent] Signal Versions on [Agent] Session Record and [Agent] Evaluator ID / [Agent] Evaluator Version on [Agent] Evaluator Result to see which evaluators changed. |
| `[Agent] Evaluated At` | number | No | Epoch milliseconds when this enrichment/evaluation was computed. |
| `[Agent] Evaluator Model` | string | No | LLM model identifier used to produce this evaluator output. |
| `[Agent] Score Label` | string | No | Direction-neutral magnitude label derived from score value. Default 5-tier: very_high (>=0.8), high (>=0.6), moderate (>=0.4), low (>=0.2), very_low (>=0.0). Server-side only. |

<a id="server-side-session-record-properties"></a>
### Server-Side: Session Record Properties

`[Agent] Session Record` is emitted automatically by the server-side enrichment pipeline — do not send this event from your code.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
| `[Agent] Agent ID` | string | Yes | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
| `[Agent] Org ID` | string | Yes | The Amplitude organization ID owning the project where this event is tracked. |
| `[Agent] Customer Org ID` | string | No | Organization ID for multi-tenant platforms. Enables account-level group analytics. |
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
| `[Agent] Taxonomy Version` | string | Yes | ID of the evaluation configuration published for the org when this enrichment ran. It changes whenever any evaluator in the org is published, even if this agent's evaluators did not change, so it is not a per-evaluator version: use [Agent] Signal Versions on [Agent] Session Record and [Agent] Evaluator ID / [Agent] Evaluator Version on [Agent] Evaluator Result to see which evaluators changed. |
| `[Agent] Evaluated At` | number | Yes | Epoch milliseconds when this enrichment/evaluation was computed. |
| `[Agent] Signal Versions` | string[] | No | Signals (Amplitude's built-in evals) that produced a result for this session, each as '<signal_id>@<version>' (e.g. 'task_completion@2'). Changes only when Amplitude updates one of those signals, unlike [Agent] Taxonomy Version, which changes on every evaluator publish in the org. Custom evaluators carry their version on [Agent] Evaluator Result. JSON array of strings. |
| `[Agent] Turn Count` | number | Yes | Number of conversation turns in this session. |
| `[Agent] Session Total Tokens` | number | No | Total LLM tokens consumed across all turns in this session. |
| `[Agent] Session Avg Latency Ms` | number | No | Average AI response latency in milliseconds across the session. |
| `[Agent] Request Complexity` | string | No | Complexity classification of the user's request: 'simple', 'moderate', 'complex', or 'ambiguous'. |
| `[Agent] Has Task Failure` | boolean | Yes | Whether the agent failed to complete the user's request. |
| `[Agent] Has Negative Feedback` | boolean | Yes | Whether the user expressed dissatisfaction during the session. |
| `[Agent] Has Technical Failure` | boolean | Yes | Whether technical errors occurred (tool timeouts, API failures, etc.). |
| `[Agent] Has Data Quality Issues` | boolean | Yes | Whether the AI output had data quality problems (wrong data, hallucinations, etc.). |
| `[Agent] Models Used` | string[] | No | LLM models used in this session. JSON array of strings. |
| `[Agent] Root Agent Name` | string | No | Entry-point agent in multi-agent flows. |
| `[Agent] Agent Chain Depth` | number | No | Number of agents in the delegation chain. |
| `[Agent] Task Failure Type` | string | No | Specific failure type when has_task_failure is true (e.g., 'wrong_answer', 'unable_to_complete'). |
| `[Agent] Technical Error Count` | number | No | Count of technical errors that occurred during the session. |
| `[Agent] Error Categories` | string[] | No | Categorized error types (e.g., 'chart_not_found', 'timeout'). JSON array of strings. |
| `[Agent] User Friction` | string[] | No | Detected user friction patterns (e.g., 'retry_storm', 'clarification_loop', 'early_abandonment'). JSON array of strings. |
| `[Agent] Session Cost USD` | number | No | Total LLM cost in USD for this AI session (aggregated from per-message costs). |
| `[Agent] Close Reason` | string | No | Why this session record fired: 'explicit_close' (the agent called session.close()) or 'timeout' (idle/max-duration/upload). Distinguishes a terminal record from a point-in-time snapshot. |
| `[Agent] Quality Score` | number | No | Overall quality score (0.0-1.0) computed by the enrichment pipeline for this session. |
| `[Agent] Sentiment Score` | number | No | User sentiment score (0.0-1.0) inferred from the conversation by the enrichment pipeline. |
| `[Agent] Task Failure Reason` | string | No | Explanation of why the task failed when has_task_failure is true (e.g., 'chart data source unavailable'). |
| `[Agent] Agent Chain` | string[] | No | Serialized JSON array of agent IDs representing the delegation chain in multi-agent flows. |
| `[Agent] Project ID` | string | No | Amplitude project ID that owns the AI session being evaluated. |
| `[Agent] Has User Feedback` | boolean | Yes | Whether the session received explicit user feedback (thumbs up/down, rating). |
| `[Agent] User Score` | number | No | Aggregate user feedback score for the session (0.0-1.0). Present only when has_user_feedback is true. |
| `[Agent] Agent Version` | string | No | Agent code version (e.g., 'v4.2'). Enables version-over-version quality comparison. |
| `[Agent] Agent Description` | string | No | Human-readable description of the agent's purpose (e.g., 'Handles user chat requests via OpenAI GPT-4o'). Enables observability-driven agent registry from event streams. |
| `[Agent] Session Trigger` | string | No | Session dimension: how the session started. One of 'interactive', 'headless', 'ci', 'scheduled' or 'event'; unset when the harness can't tell. |
| `[Agent] Context` | string | No | Serialized JSON dict of arbitrary segmentation dimensions (surface, prompt_revision, etc.). Experiment assignment has its own [Agent] Experiment properties. |
| `[Agent] Experiments` | string[] | No | Every experiment arm the session ran under, one 'key:variant' string each (at most 32), as the union of [Agent] Experiments across the session's SDK events in first-seen order. A turn-unit experiment that changed arm mid-session contributes each arm. Keys and variants are matched case-sensitively, as the SDK sends them. Split a session-grain metric by it to compare arms. |
| `[Agent] Experiment Key` | string | No | Session dimension: the experiment key, present only when the session ran under exactly one arm (one 'key:variant'). A session that saw two arms of the same experiment does not carry it. |
| `[Agent] Experiment Variant` | string | No | Session dimension: the variant, present only when the session ran under exactly one arm (one 'key:variant'). |
| `[Agent] Experiment Unit` | string | No | Session dimension: the unit of the one arm the session ran under ('agent_session', 'turn' or 'span'), when the SDK events named it. |
| `[Agent] Experiment Provider` | string | No | Session dimension: the provider of the one arm the session ran under, when the SDK events named it. |
| `[Agent] Experiment Source` | string | No | Where the session's experiment arms came from: 'sdk', 'otel', 'http' or 'warehouse' when the events carried [Agent] Experiments over that transport; 'context' when they were read from the session's [Agent] Context tags. Present only when the session ran under an experiment and the transport is known; a session whose metadata names no transport carries the arms without this property. |
| `[Agent] Experiment Overflow` | number | No | Distinct experiment arms dropped because the session ran under more than 32. Present only when nonzero; the arms listed in [Agent] Experiments understate the session by this many. At least 1 when the span read stopped at its size bound, so the count is a lower bound. |
| `[Agent] Task Completed` | string | No | OOTB detector result: 'True' if agent completed user's request, 'False' otherwise. |
| `[Agent] Task Completed Rationale` | string | No | Brief rationale for the task completion determination. |
| `[Agent] Task Completed Evidence` | string | No | Supporting evidence for the task completion determination. |
| `[Agent] Response Quality` | string | No | OOTB detector result: 'True' if response was accurate/clear/helpful, 'False' otherwise. |
| `[Agent] Response Quality Rationale` | string | No | Brief rationale for the response quality determination. |
| `[Agent] Response Quality Evidence` | string | No | Supporting evidence for the response quality determination. |
| `[Agent] Session Outcome` | string | No | OOTB classifier result for how the session ended (e.g., 'Response Provided', 'Unable to Complete', 'Escalated'). Only emitted when the outcome evaluator is enabled; disabled in defaults.json. |
| `[Agent] Session Outcome Rationale` | string | No | Brief rationale for the session outcome classification. Only emitted when the outcome evaluator is enabled; disabled in defaults.json. |
| `[Agent] Session Safety` | string | No | OOTB classifier result for session safety (e.g., 'Normal', 'Off Topic', 'Prompt Injection', 'Abuse'). |
| `[Agent] Session Safety Rationale` | string | No | Brief rationale for the session safety classification. |
| `[Agent] User Intent` | string | No | OOTB classifier result for user's primary intent (e.g., 'Information Request', 'Task Execution', 'Content Creation'). |
| `[Agent] User Intent Rationale` | string | No | Brief rationale for the user intent classification. |
| `[Agent] Has User Friction` | boolean | No | OOTB detector result: whether conversation exhibited user friction (retry storms, clarification loops, etc.). |
| `[Agent] User Friction Rationale` | string | No | Brief rationale for user friction detection. |
| `[Agent] Detected User Friction` | string[] | No | Specific user friction patterns detected (e.g., 'retry_storm', 'tool_cascade_fail'). JSON array. |
| `[Agent] Detected Data Quality Issues` | string[] | No | Specific data quality issues detected. JSON array. |
| `[Agent] Data Quality Rationale` | string | No | Brief rationale for the data quality determination, including typed tool-call leaves. |
| `[Agent] Data Quality Evidence` | string | No | Supporting evidence for the data quality determination. Truncated when written to Nova. |
| `[Agent] Detected Negative Feedback` | string[] | No | Specific negative-feedback signals detected (e.g., explicit complaint phrases). JSON array. |
| `[Agent] Channel` | string | No | 'voice' when any turn in the session is a voice turn. Omitted on chat sessions. |
| `[Agent] Voice Direction` | string | No | Call direction from the first voice turn that reports one: 'inbound', 'outbound', or 'in_app'. |
| `[Agent] Voice Avg Time To First Audio Ms` | number | No | Average time to first audio across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Max Time To First Audio Ms` | number | No | Slowest time to first audio in the session, in milliseconds. |
| `[Agent] Voice Interruption Count` | number | No | Number of voice turns that were spoken over by either side. |
| `[Agent] Voice User Interruption Count` | number | No | Number of voice turns the caller spoke over (barge-in). |
| `[Agent] Voice Yield Count` | number | No | Number of agent turns cut off because the caller spoke. |
| `[Agent] Voice Final End Of Turn Reason` | string | No | End of turn reason of the last voice turn that reports one: completed, interrupted, silence, transferred, or error. |
| `[Agent] Voice Total Speech Duration Ms` | number | No | Total milliseconds of speech across the session's voice turns that report it. |
| `[Agent] Voice Avg STT Ms` | number | No | Average speech-to-text latency across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice P95 STT Ms` | number | No | 95th percentile speech-to-text latency across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Avg LLM TTFT Ms` | number | No | Average model time to first token across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice P95 LLM TTFT Ms` | number | No | 95th percentile model time to first token across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Avg TTS TTFB Ms` | number | No | Average speech synthesizer time to first byte across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice P95 TTS TTFB Ms` | number | No | 95th percentile speech synthesizer time to first byte across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Slowest Stage` | string | No | Pipeline stage with the highest average latency: 'stt', 'llm', or 'tts'. Omitted when no turn reports stage latency. |
| `[Agent] Voice Total Pre Reply Silence Ms` | number | No | Total milliseconds of silence the caller heard before agent replies, across the turns that report it. |
| `[Agent] Voice Max Pre Reply Silence Ms` | number | No | Longest silence, in milliseconds, the caller heard before an agent reply. Omitted when no turn reports it. |
| `[Agent] Voice Overlap Count` | number | No | Number of voice turns that report any overlap with the other side's speech. |
| `[Agent] Voice Max Overlap Ms` | number | No | Longest overlap, in milliseconds, between the caller and the agent. Omitted when no turn overlapped. |
| `[Agent] Voice Total Overlap Ms` | number | No | Total milliseconds the caller and the agent spoke over each other. Omitted when no turn overlapped. |
| `[Agent] Voice Call Duration Ms` | number | No | Milliseconds from the earliest turn start offset to the latest turn end offset. Omitted when turns report no offsets. |
| `[Agent] Voice First Reply Ms` | number | No | Time to first audio of the first agent turn that reports it. Omitted when no agent turn reports it. |
| `[Agent] Voice Ended Mid Reply` | boolean | No | True when the session's last agent turn has an end of turn reason other than completed. Omitted when that turn reports no end of turn reason. |

<a id="server-side-topic-classification-properties-deprecated"></a>
### Server-Side: Topic Classification Properties (deprecated)

`[Agent] Topic Classification` is **DEPRECATED** — replaced by `[Agent] Evaluator Result`. Retained in the catalog for historical data only; the enrichment pipeline no longer emits this event.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
| `[Agent] Agent ID` | string | Yes | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
| `[Agent] Org ID` | string | Yes | The Amplitude organization ID owning the project where this event is tracked. |
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
| `[Agent] Taxonomy Version` | string | Yes | ID of the evaluation configuration published for the org when this enrichment ran. It changes whenever any evaluator in the org is published, even if this agent's evaluators did not change, so it is not a per-evaluator version: use [Agent] Signal Versions on [Agent] Session Record and [Agent] Evaluator ID / [Agent] Evaluator Version on [Agent] Evaluator Result to see which evaluators changed. |
| `[Agent] Evaluated At` | number | Yes | Epoch milliseconds when this enrichment/evaluation was computed. |
| `[Agent] Topic` | string | Yes | Which topic model this classification is for (e.g., 'product_area', 'query_intent', 'error_domain'). |
| `[Agent] Selection Mode` | string | Yes | Whether this topic model uses 'single' (MECE) or 'multiple' (multi-label) selection. |
| `[Agent] Primary Label` | string | No | Primary classification value (e.g., 'charts', 'billing_issues'). |
| `[Agent] Secondary Label` | string[] | No | Secondary classifications for multi-label topics. JSON array of strings. |
| `[Agent] Subcategories` | string[] | No | Subcategories for finer classification within the primary topic (e.g., 'TREND_ANALYSIS', 'WRONG_EVENT'). JSON array of strings. |
| `[Agent] Evaluator Model` | string | No | LLM model identifier used to produce this evaluator output. |

<a id="server-side-evaluator-result-properties"></a>
### Server-Side: Evaluator Result Properties

`[Agent] Evaluator Result` is emitted automatically by the server-side enrichment pipeline — do not send this event from your code.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
| `[Agent] Agent ID` | string | Yes | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
| `[Agent] Org ID` | string | Yes | The Amplitude organization ID owning the project where this event is tracked. |
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
| `[Agent] Evaluator ID` | string | No | Stable ID of the custom evaluator, the same across all of its versions and renames. Set together with [Agent] Evaluator Version. |
| `[Agent] Evaluator Version` | string | No | Version of this evaluator that produced the result (1, 2, 3...), bumped each time it is edited and promoted. Set together with [Agent] Evaluator ID. Events from before evaluator versions were tracked hold an org-wide configuration ID here instead. |
| `[Agent] Evaluated At` | number | Yes | Epoch milliseconds when this enrichment/evaluation was computed. |
| `[Agent] Evaluator Name` | string | Yes | Human-readable evaluator name (primary group-by for users). |
| `[Agent] Evaluator Output Type` | string | Yes | Discriminator for the evaluator kind: 'classification', 'score', or 'binary'. |
| `[Agent] Selection Mode` | string | No | Whether this evaluator uses 'single' or 'multiple' selection. Classification only. |
| `[Agent] Primary Label` | string | No | Primary classification label. Classification only. |
| `[Agent] Secondary Label` | string[] | No | Secondary labels for multi-label evaluators. Classification only. |
| `[Agent] Subcategories` | string[] | No | Finer classification within the primary label. Classification only. |
| `[Agent] Binary Label` | boolean | No | Whether the condition was detected. Binary only. |
| `[Agent] Binary Value` | number | No | Numeric mirror of Binary Label (1=True, 0=False) for uniform charting. Binary only. |
| `[Agent] Decision Probability` | number | No | Calibrated P(true) for binary Decision evaluators or P(chosen) for categorical Decision evaluators. |
| `[Agent] Decision Confidence` | number | No | TypeSafe confidence statistic for categorical Decision evaluators. |
| `[Agent] Class Probabilities` | any[] | No | Full label/probability distribution returned by a categorical Decision evaluator. |
| `[Agent] Evaluation Method` | string | No | Evaluator execution method: llm, code, or decision_model. |
| `[Agent] Score Value` | number | No | Numeric score assigned by the evaluator. Score only. |
| `[Agent] Score Label` | string | No | Human-readable tier (e.g., 'good', 'poor'). Score only. |
| `[Agent] Target ID` | string | No | ID of the scored entity (session_id). Score only. |
| `[Agent] Target Type` | string | No | Type of scored entity (e.g., 'session'). Score only. |
| `[Agent] Improvement Opportunities` | string | No | Suggestions for how the agent could improve. Score only. |
| `[Agent] Rationale` | string | No | Explanation or calibrated distribution summary for the result. |
| `[Agent] Evidence` | string | No | Supporting evidence (quotes, observations) for the result. |
| `[Agent] Evaluator Model` | string | No | LLM model identifier used to produce this evaluator output. |
| `[Agent] Evaluator Enrichment Cost USD` | number | No | Cost in USD of running this specific evaluator's LLM inference. Customer-attributable enrichment cost for this evaluator; distinct from the session's own LLM cost ([Agent] Session Cost USD). |
| `[Agent] Customer Org ID` | string | No | Organization ID for multi-tenant platforms. Enables account-level group analytics. |
| `[Agent] Turn Count` | number | No | Session dimension: number of conversation turns in this session. |
| `[Agent] Session Total Tokens` | number | No | Session dimension: total LLM tokens consumed across all turns in this session. |
| `[Agent] Session Avg Latency Ms` | number | No | Session dimension: average AI response latency in milliseconds across the session. |
| `[Agent] Session Cost USD` | number | No | Session dimension: total LLM cost in USD for this AI session. |
| `[Agent] Has Technical Failure` | boolean | No | Session dimension: whether technical errors occurred during the session. |
| `[Agent] Has Negative Feedback` | boolean | No | Session dimension: whether the user expressed dissatisfaction during the session. |
| `[Agent] Has Data Quality Issues` | boolean | No | Session dimension: whether the AI output had data quality problems. |
| `[Agent] Has User Feedback` | boolean | No | Session dimension: whether the session received explicit user feedback. |
| `[Agent] Technical Error Count` | number | No | Session dimension: count of technical errors during the session. |
| `[Agent] Models Used` | string[] | No | Session dimension: LLM models used in this session. JSON array of strings. |
| `[Agent] Project ID` | string | No | Session dimension: Amplitude project ID that owns the AI session. |
| `[Agent] Root Agent Name` | string | No | Session dimension: entry-point agent in multi-agent flows. |
| `[Agent] Agent Chain Depth` | number | No | Session dimension: number of agents in the delegation chain. |
| `[Agent] User Score` | number | No | Session dimension: aggregate user feedback score for the session (0.0-1.0). |
| `[Agent] Agent Version` | string | No | Agent code version (e.g., 'v4.2'). Enables version-over-version quality comparison. |
| `[Agent] Session Trigger` | string | No | Session dimension: how the session started. One of 'interactive', 'headless', 'ci', 'scheduled' or 'event'; unset when the harness can't tell. |
| `[Agent] Close Reason` | string | No | Session dimension: why the session record fired ('explicit_close' or 'timeout'). |
| `[Agent] Experiments` | string[] | No | Every experiment arm the session ran under, one 'key:variant' string each (at most 32), as the union of [Agent] Experiments across the session's SDK events in first-seen order. A turn-unit experiment that changed arm mid-session contributes each arm. Keys and variants are matched case-sensitively, as the SDK sends them. Split a session-grain metric by it to compare arms. |
| `[Agent] Experiment Key` | string | No | Session dimension: the experiment key, present only when the session ran under exactly one arm (one 'key:variant'). A session that saw two arms of the same experiment does not carry it. |
| `[Agent] Experiment Variant` | string | No | Session dimension: the variant, present only when the session ran under exactly one arm (one 'key:variant'). |
| `[Agent] Experiment Unit` | string | No | Session dimension: the unit of the one arm the session ran under ('agent_session', 'turn' or 'span'), when the SDK events named it. |
| `[Agent] Experiment Provider` | string | No | Session dimension: the provider of the one arm the session ran under, when the SDK events named it. |
| `[Agent] Topic Summary` | string | No | Session dimension: concise one-sentence factual summary of what the user was trying to accomplish. Produced by the OOTB batched eval pass-1 at zero marginal cost. |
| `[Agent] Channel` | string | No | Session dimension: 'voice' when any turn in the session is a voice turn. Omitted on chat sessions. |
| `[Agent] Voice Direction` | string | No | Session dimension: Call direction from the first voice turn that reports one: 'inbound', 'outbound', or 'in_app'. |
| `[Agent] Voice Avg Time To First Audio Ms` | number | No | Session dimension: Average time to first audio across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Max Time To First Audio Ms` | number | No | Session dimension: Slowest time to first audio in the session, in milliseconds. |
| `[Agent] Voice Interruption Count` | number | No | Session dimension: Number of voice turns that were spoken over by either side. |
| `[Agent] Voice User Interruption Count` | number | No | Session dimension: Number of voice turns the caller spoke over (barge-in). |
| `[Agent] Voice Yield Count` | number | No | Session dimension: Number of agent turns cut off because the caller spoke. |
| `[Agent] Voice Final End Of Turn Reason` | string | No | Session dimension: End of turn reason of the last voice turn that reports one: completed, interrupted, silence, transferred, or error. |
| `[Agent] Voice Total Speech Duration Ms` | number | No | Session dimension: Total milliseconds of speech across the session's voice turns that report it. |
| `[Agent] Voice Avg STT Ms` | number | No | Session dimension: Average speech-to-text latency across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice P95 STT Ms` | number | No | Session dimension: 95th percentile speech-to-text latency across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Avg LLM TTFT Ms` | number | No | Session dimension: Average model time to first token across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice P95 LLM TTFT Ms` | number | No | Session dimension: 95th percentile model time to first token across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Avg TTS TTFB Ms` | number | No | Session dimension: Average speech synthesizer time to first byte across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice P95 TTS TTFB Ms` | number | No | Session dimension: 95th percentile speech synthesizer time to first byte across the session's agent turns that report it, in milliseconds. |
| `[Agent] Voice Slowest Stage` | string | No | Session dimension: Pipeline stage with the highest average latency: 'stt', 'llm', or 'tts'. Omitted when no turn reports stage latency. |
| `[Agent] Voice Total Pre Reply Silence Ms` | number | No | Session dimension: Total milliseconds of silence the caller heard before agent replies, across the turns that report it. |
| `[Agent] Voice Max Pre Reply Silence Ms` | number | No | Session dimension: Longest silence, in milliseconds, the caller heard before an agent reply. Omitted when no turn reports it. |
| `[Agent] Voice Overlap Count` | number | No | Session dimension: Number of voice turns that report any overlap with the other side's speech. |
| `[Agent] Voice Max Overlap Ms` | number | No | Session dimension: Longest overlap, in milliseconds, between the caller and the agent. Omitted when no turn overlapped. |
| `[Agent] Voice Total Overlap Ms` | number | No | Session dimension: Total milliseconds the caller and the agent spoke over each other. Omitted when no turn overlapped. |
| `[Agent] Voice Call Duration Ms` | number | No | Session dimension: Milliseconds from the earliest turn start offset to the latest turn end offset. Omitted when turns report no offsets. |
| `[Agent] Voice First Reply Ms` | number | No | Session dimension: Time to first audio of the first agent turn that reports it. Omitted when no agent turn reports it. |
| `[Agent] Voice Ended Mid Reply` | boolean | No | Session dimension: True when the session's last agent turn has an end of turn reason other than completed. Omitted when that turn reports no end of turn reason. |

<a id="agent-cluster-assignment-properties"></a>
### [Agent] Cluster Assignment Properties

Event-specific properties for `[Agent] Cluster Assignment` (in addition to common properties above).

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `[Agent] Cluster Label` | string | No | Human-readable label summarizing the topic of the assigned cluster. |
| `[Agent] Cluster ID` | string | No | Internal numeric cluster identifier within the clustering run. |
| `[Agent] Stable Cluster ID` | string | No | Cross-run stable identifier for this topic. Persists across re-clustering runs via centroid similarity matching (Hungarian algorithm). |
| `[Agent] Distance to Centroid` | number | No | Cosine distance from the session embedding to the cluster centroid. Lower is more central. |
| `[Agent] Is Noise` | boolean | No | True if HDBSCAN classified this session as noise (not belonging to any cluster). |
| `[Agent] Clustering Run ID` | string | No | ID of the clustering run that produced this assignment. |
<!-- END EVENT PROPERTY REFERENCE -->

<a id="license"></a>
## License

[MIT](https://opensource.org/licenses/MIT)

---

> **About this README.** The long-form SDK reference previously hosted here moved to the [canonical docs page](https://amplitude.com/docs/sdks/agent-analytics/sdk) so npm, PyPI, and the in-product docs surface stay aligned. The full instrumentation guide consumed by AI coding agents is `amplitude-ai.md`, bundled with the package; `amplitude-ai` prints a prompt that points your coding agent at it.
