Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,81 @@ If you call Jev through the <PlatformLink to="/configuration/integrations/typesa
<Include name="tracing/ai-agents-module/evaluation-span" />
</Expandable>

## Extraction Span

This span represents an extraction of typed fields from an input, such as pulling the invoice number, due date, and total out of an invoice. Extraction models like Cloudflare Clairic produce these spans. They return typed values instead of a chat reply, so they get their own span type.

Sentry identifies an extraction by `gen_ai.operation.name: "extract"`.

```javascript
const extractionRequest = {
state:
"Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.",
fields: {
invoice_number: { type: "text" },
due_date: {
type: "date",
instructions: "Issue date plus the net payment days",
},
total: {
type: "number",
decimals: 2,
unit: "USD",
instructions: "Amount due including shipping",
},
contacts: { type: "list", items: "text" },
},
};

await Sentry.startSpan(
{
op: "gen_ai.extract",
name: "extract @cf/cloudflare/clairic",
attributes: {
"gen_ai.operation.name": "extract",
"gen_ai.request.model": "@cf/cloudflare/clairic",
"gen_ai.provider.name": "cloudflare.workers_ai",
"gen_ai.agent.name": "Billing Agent", // when this extraction runs under an agent
// The request body as you send it, without `model`
"gen_ai.input.messages": JSON.stringify([extractionRequest]),
},
},
async (span) => {
// Call your extraction model; map its response into span attributes below
const { model, usage, ...response } = await yourExtractor.extract({
model: "@cf/cloudflare/clairic",
...extractionRequest,
});

span.setAttributes({
"gen_ai.response.model": model, // e.g. "clairic"
// The response body as the provider returned it, without `model` and `usage`:
// { answers: { total: { type: "number", value: 1080, confidence: 0.9999 }, ... }, truncated: false }
"gen_ai.output.messages": JSON.stringify([response]),
"gen_ai.usage.input_tokens": usage.input_tokens,
"gen_ai.usage.output_tokens": usage.output_tokens,
});
return response;
}
);
```

The input is a single object holding the request body: `state` is the text the model reads, and each entry in `fields` describes one field to extract. The output is a single object holding the response body, with `answers` keyed by those same field names. Record both as you sent and received them, without changing the values. If the request includes images, replace each one with the string `"[Blob substitute]"` instead of recording the image data. See the attributes below for the full shape.

Text and list answers include `[start, end]` character offsets into `state`. Sentry uses them to highlight each extracted value in the source text, so keep them as the provider returned them.

Pass the raw provider model string in `gen_ai.request.model` (and `gen_ai.response.model`) so Sentry can resolve the model icon and any [model costs](/product/agents/costs/).

<Alert>

If you call Clairic through the [Cloudflare Workers AI binding](/platforms/javascript/guides/cloudflare/agent-tracing/workers-ai/) with `@sentry/cloudflare` 11.10.0 or later, these spans are created automatically, so you don't need to instrument them by hand.

</Alert>

<Expandable title="Extraction span attributes">
<Include name="tracing/ai-agents-module/extraction-span" />
</Expandable>

## Memory Span

This span represents a read from, write to, or management operation against an agent's persistent memory store, such as retrieving relevant memories before a model call or saving new ones after it.
Expand Down
65 changes: 65 additions & 0 deletions docs/platforms/python/agent-tracing/manual-instrumentation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,71 @@ If you call Jev through the <PlatformLink to="/agent-tracing/typesafe/">TypeSafe
<Include name="tracing/ai-agents-module/evaluation-span" />
</Expandable>

## Extraction Span

This span represents an extraction of typed fields from an input, such as pulling the invoice number, due date, and total out of an invoice. Extraction models like Cloudflare Clairic produce these spans. They return typed values instead of a chat reply, so they get their own span type.

Sentry identifies an extraction by `gen_ai.operation.name: "extract"`.

```python
import json
import sentry_sdk

extraction_request = {
"state": (
"Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. "
"4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah."
),
"fields": {
"invoice_number": {"type": "text"},
"due_date": {
"type": "date",
"instructions": "Issue date plus the net payment days",
},
"total": {
"type": "number",
"decimals": 2,
"unit": "USD",
"instructions": "Amount due including shipping",
},
"contacts": {"type": "list", "items": "text"},
},
}

with sentry_sdk.start_span(
op="gen_ai.extract",
name="extract @cf/cloudflare/clairic",
attributes={
"gen_ai.operation.name": "extract",
"gen_ai.request.model": "@cf/cloudflare/clairic",
"gen_ai.provider.name": "cloudflare.workers_ai",
"gen_ai.agent.name": "Billing Agent", # when this extraction runs under an agent
# The request body as you send it, without "model"
"gen_ai.input.messages": json.dumps([extraction_request]),
},
) as span:
# Call your extraction model; map its response into span attributes below
result = your_extractor.extract(model="@cf/cloudflare/clairic", **extraction_request)
response = {k: v for k, v in result.items() if k not in ("model", "usage")}

span.set_data("gen_ai.response.model", result["model"]) # e.g. "clairic"
# The response body as the provider returned it, without "model" and "usage":
# {"answers": {"total": {"type": "number", "value": 1080, "confidence": 0.9999}, ...}, "truncated": False}
span.set_data("gen_ai.output.messages", json.dumps([response]))
span.set_data("gen_ai.usage.input_tokens", result["usage"]["input_tokens"])
span.set_data("gen_ai.usage.output_tokens", result["usage"]["output_tokens"])
```

The input is a single object holding the request body: `state` is the text the model reads, and each entry in `fields` describes one field to extract. The output is a single object holding the response body, with `answers` keyed by those same field names. Record both as you sent and received them, without changing the values. If the request includes images, replace each one with the string `"[Blob substitute]"` instead of recording the image data. See the attributes below for the full shape.

Text and list answers include `[start, end]` character offsets into `state`. Sentry uses them to highlight each extracted value in the source text, so keep them as the provider returned them.

Pass the raw provider model string in `gen_ai.request.model` (and `gen_ai.response.model`) so Sentry can resolve the model icon and any [model costs](/product/agents/costs/).

<Expandable title="Extraction span attributes">
<Include name="tracing/ai-agents-module/extraction-span" />
</Expandable>

## Memory Span

This span represents a read from, write to, or management operation against an agent's persistent memory store, such as retrieving relevant memories before a model call or saving new ones after it.
Expand Down
34 changes: 34 additions & 0 deletions includes/tracing/ai-agents-module/extraction-span.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Describes an extraction of typed fields from an input, such as the total and due date of an invoice (for example a Cloudflare Clairic call).

- The span `op` (transaction mode) or the span's `sentry.op` attribute (stream mode) MUST be `"gen_ai.extract"`.
- The span `name` SHOULD be `"extract {gen_ai.request.model}"`. (e.g. `"extract @cf/cloudflare/clairic"`)
- The `gen_ai.operation.name` attribute MUST be `"extract"`.
- The `gen_ai.request.model` attribute SHOULD be the requested extraction model. (e.g. `"@cf/cloudflare/clairic"`)
- The `gen_ai.response.model` attribute SHOULD be the concrete model that responded. Omit it when the provider doesn't report one. (e.g. `"clairic"`)
- The `gen_ai.provider.name` attribute SHOULD be set to the provider. (e.g. `"cloudflare.workers_ai"`)
- The `gen_ai.agent.name` attribute SHOULD be set to the agent's name when the extraction runs under an agent. (e.g. `"Billing Agent"`)

Additional attributes on the span:

| Data Attribute | Type | Requirement Level | Description | Example |
| :--------------------------- | :----- | :---------------- | :------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------ |
| `gen_ai.input.messages` | string | optional | A single object holding the request body, without `model` (stringified JSON). **[0]** | `'[{"state": "Invoice INV-2291 ...", "fields": {"total": {"type": "number", "unit": "USD"}}}]'` |
| `gen_ai.output.messages` | string | optional | A single object holding the response body, without `model` and `usage` (stringified JSON). **[1]** | `'[{"answers": {"total": {"type": "number", "value": 1080, "confidence": 0.99}}, "truncated": false}]'` |
| `gen_ai.usage.input_tokens` | int | optional | The number of input tokens used by the extraction, including all passes. | `655` |
| `gen_ai.usage.output_tokens` | int | optional | The number of output tokens used by the extraction. | `0` |

Don't send the other usage counts that the provider reports (such as Clairic's `state_tokens`, `image_tokens`, `schema_tokens`, and `passes`). `gen_ai.usage.input_tokens` already includes them.

**[0]** The input is the request body as the caller passed it. Don't change the values, and leave out the keys the caller didn't set:

| Field | Type | Description |
| :--------------- | :----------- | :-------------------------------------------------------------------------------------------------- |
| `state` | string | The text the model reads. Required when there are no `images`. Text answers point into this string. |
| `fields` | object | Required. The field specs, keyed by field name. |
| `images` | string array | One `"[Blob substitute]"` string for each image. Never send the image data. |
| `reference_date` | string | The ISO date that anchors dates written without a year. |
| `options` | object | Request options, such as `include_quantiles` or `return_state_text`. |

Each field spec has a `type`: `text`, `number`, `date`, `time`, `datetime`, `duration`, `list`, `estimate`, or `forecast`. It can also carry `instructions` and type-specific settings, such as `unit`, `integer`, and `decimals` for numbers, `items` for lists (`text`, `number`, or `date`), and `min`, `max`, and `step` for estimates and forecasts.

**[1]** The output is the response body as the provider returned it. Don't change the values. `answers` has one answer for each field in the input, keyed by field name, and `truncated` is copied as returned. Each answer has the field's `type` and a `value`, which is `null` when the input doesn't determine it. Answers can also carry `guess`, `p_null`, and `confidence`. `text` answers carry a `span` and `list` answers carry `spans`: `[start, end]` character offsets into `state` (or into `state_text`, if the provider returns it), where `end` is exclusive. `estimate` answers carry `mean`, `median`, `p10`, and `p90`, and `forecast` answers carry one estimate per step in `steps`.
Loading