diff --git a/javascript/sentry-conventions/src/attributes.ts b/javascript/sentry-conventions/src/attributes.ts index e4a8faaf..674eccfa 100644 --- a/javascript/sentry-conventions/src/attributes.ts +++ b/javascript/sentry-conventions/src/attributes.ts @@ -8089,7 +8089,7 @@ export type GEN_AI_FUNCTION_ID_TYPE = string; // Path: model/attributes/gen_ai/gen_ai__input__messages.json /** - * The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", state: ..., questions: {...}}` with the evaluated state and the questions keyed by name, as the caller passed them. `gen_ai.input.messages` + * The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. The first example shows this shape. Some operations use a different shape. See the additional context. `gen_ai.input.messages` * * Attribute Value Type: `string` {@link GEN_AI_INPUT_MESSAGES_TYPE} * @@ -8101,6 +8101,8 @@ export type GEN_AI_FUNCTION_ID_TYPE = string; * Aliases: {@link AI_TEXTS} `ai.texts`, {@link AI_PROMPT_MESSAGES} `ai.prompt.messages`, {@link GEN_AI_PROMPT} `gen_ai.prompt`, {@link AI_PROMPT} `ai.prompt` * * @example "[{\"role\": \"user\", \"parts\": [{\"type\": \"text\", \"content\": \"Weather in Paris?\"}]}, {\"role\": \"assistant\", \"parts\": [{\"type\": \"tool_call\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]}, {\"role\": \"tool\", \"parts\": [{\"type\": \"tool_call_response\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"result\": \"rainy, 57°F\"}]}]" + * @example "[{\"state\": \"I cannot log in, and I also want a refund for last month.\", \"questions\": {\"authIssue\": {\"type\": \"noul\", \"instructions\": \"Is there a login problem?\"}, \"department\": {\"type\": \"choice\", \"instructions\": \"Which team should handle this?\", \"criteria\": {\"billing\": \"Charges and refunds\", \"technical\": \"Bugs and outages\"}}}}]" + * @example "[{\"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\"}, \"po_number\": {\"type\": \"text\", \"instructions\": \"Purchase order number\"}, \"days_to_pay\": {\"type\": \"estimate\", \"min\": 0, \"max\": 120, \"step\": 1, \"instructions\": \"Days after issue until this invoice is paid\"}}}]" */ export const GEN_AI_INPUT_MESSAGES = 'gen_ai.input.messages'; @@ -8240,6 +8242,7 @@ export type GEN_AI_MEMORY_STORE_ID_TYPE = string; * - `embeddings` * - `evaluate` * - `execute_tool` + * - `extract` * - `generate_content` * - `invoke_agent` * - `search_memory` @@ -8288,7 +8291,7 @@ export type GEN_AI_OPERATION_TYPE_TYPE = string; // Path: model/attributes/gen_ai/gen_ai__output__messages.json /** - * The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", answers: {...}}` with the answers keyed by question name, as the provider returned them. `gen_ai.output.messages` + * The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. The first example shows this shape. Some operations use a different shape. See the additional context. `gen_ai.output.messages` * * Attribute Value Type: `string` {@link GEN_AI_OUTPUT_MESSAGES_TYPE} * @@ -8300,6 +8303,8 @@ export type GEN_AI_OPERATION_TYPE_TYPE = string; * Aliases: {@link AI_RESPONSE_TOOLCALLS} `ai.response.toolCalls`, {@link AI_RESPONSE_TEXT} `ai.response.text` * * @example "[{\"role\": \"assistant\", \"parts\": [{\"type\": \"text\", \"content\": \"The weather in Paris is currently rainy with a temperature of 57°F.\"}], \"finish_reason\": \"stop\"}]" + * @example "[{\"answers\": {\"authIssue\": {\"type\": \"noul\", \"noul\": 0.98}, \"department\": {\"type\": \"choice\", \"choice\": \"billing\", \"confidence\": 0.28, \"probabilities\": {\"billing\": 0.64, \"technical\": 0.36}}}}]" + * @example "[{\"answers\": {\"invoice_number\": {\"type\": \"text\", \"p_null\": 3.8e-10, \"span\": [8, 16], \"guess\": \"INV-2291\", \"confidence\": 1, \"value\": \"INV-2291\"}, \"due_date\": {\"type\": \"date\", \"p_null\": 1.9e-08, \"guess\": \"2026-10-02\", \"confidence\": 0.9999, \"value\": \"2026-10-02\"}, \"total\": {\"type\": \"number\", \"p_null\": 2.3e-08, \"guess\": 1080, \"confidence\": 0.9999, \"value\": 1080}, \"contacts\": {\"type\": \"list\", \"value\": [\"Dana Ruiz\", \"Priya Shah\"], \"spans\": [[120, 129], [131, 141]], \"confidence\": 0.9999}, \"po_number\": {\"type\": \"text\", \"p_null\": 1, \"span\": [8, 16], \"guess\": \"INV-2291\", \"confidence\": 0.9995, \"value\": null}, \"days_to_pay\": {\"type\": \"estimate\", \"value\": 30, \"mean\": 29.535, \"median\": 30, \"p10\": 21, \"p90\": 38, \"p_below\": 5.9e-06, \"p_above\": 1.2e-05}}, \"truncated\": false}]" */ export const GEN_AI_OUTPUT_MESSAGES = 'gen_ai.output.messages'; @@ -28166,7 +28171,7 @@ export const ATTRIBUTE_METADATA: Record = { }, 'gen_ai.input.messages': { brief: - 'The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", state: ..., questions: {...}}` with the evaluated state and the questions keyed by name, as the caller passed them.', + 'The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. The first example shows this shape. Some operations use a different shape. See the additional context.', type: 'string', keys: [ 'gen_ai.input.messages', @@ -28183,13 +28188,28 @@ export const ATTRIBUTE_METADATA: Record = { visibility: 'public', example: '[{"role": "user", "parts": [{"type": "text", "content": "Weather in Paris?"}]}, {"role": "assistant", "parts": [{"type": "tool_call", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "name": "get_weather", "arguments": {"location": "Paris"}}]}, {"role": "tool", "parts": [{"type": "tool_call_response", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "result": "rainy, 57°F"}]}]', + examples: [ + '[{"role": "user", "parts": [{"type": "text", "content": "Weather in Paris?"}]}, {"role": "assistant", "parts": [{"type": "tool_call", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "name": "get_weather", "arguments": {"location": "Paris"}}]}, {"role": "tool", "parts": [{"type": "tool_call_response", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "result": "rainy, 57°F"}]}]', + '[{"state": "I cannot log in, and I also want a refund for last month.", "questions": {"authIssue": {"type": "noul", "instructions": "Is there a login problem?"}, "department": {"type": "choice", "instructions": "Which team should handle this?", "criteria": {"billing": "Charges and refunds", "technical": "Bugs and outages"}}}}]', + '[{"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"}, "po_number": {"type": "text", "instructions": "Purchase order number"}, "days_to_pay": {"type": "estimate", "min": 0, "max": 120, "step": 1, "instructions": "Days after issue until this invoice is paid"}}}]', + ], aliases: ['ai.texts', 'ai.prompt.messages', 'gen_ai.prompt', 'ai.prompt'], changelog: [ + { + version: 'next', + prs: [728], + description: + 'Describe the extraction message shape for gen_ai.extract and move operation-specific shapes to additional context', + }, { version: '0.26.0', prs: [650], description: 'Describe the evaluation message shape for gen_ai.evaluate' }, { version: '0.21.0', prs: [583], description: 'Added ai.prompt as an alias' }, { version: '0.5.0', prs: [264] }, { version: '0.4.0', prs: [221] }, ], + additionalContext: [ + 'For `gen_ai.decide` operations, the array holds one object `{state: ..., questions: {...}}`. `state` is the evaluated state. `questions` holds the questions keyed by name, as the caller passed them. Replace binary data in `state`, including `data:` URLs, with the string `"[Blob substitute]"`. The second example shows this shape.', + 'For `gen_ai.extract` operations, the array holds one object `{state?: string, fields: {: }, images?: string[], reference_date?: string, options?: {...}}`. This object is the request body as the caller passed it, without `model`. Do not change the values, and leave out the keys that the caller did not set. Do not record the image data. Replace each image with the string `"[Blob substitute]"`. `state` is the text that the model reads. `fields` holds the field specs keyed by field name. Set the model in `gen_ai.request.model`. The third example shows this shape.', + ], }, 'gen_ai.memory.query.text': { brief: @@ -28275,6 +28295,7 @@ export const ATTRIBUTE_METADATA: Record = { visibility: 'public', example: 'chat', changelog: [ + { version: 'next', prs: [728], description: "Added 'extract' to the well-known values" }, { version: '0.27.0', prs: [653, 680], @@ -28304,7 +28325,7 @@ export const ATTRIBUTE_METADATA: Record = { }, 'gen_ai.output.messages': { brief: - 'The model\'s response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", answers: {...}}` with the answers keyed by question name, as the provider returned them.', + "The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. The first example shows this shape. Some operations use a different shape. See the additional context.", type: 'string', keys: ['gen_ai.output.messages', 'ai.response.text', 'ai.response.toolCalls', 'ai.responses', 'ai.tool_calls'], applyScrubbing: { @@ -28314,11 +28335,26 @@ export const ATTRIBUTE_METADATA: Record = { visibility: 'public', example: '[{"role": "assistant", "parts": [{"type": "text", "content": "The weather in Paris is currently rainy with a temperature of 57°F."}], "finish_reason": "stop"}]', + examples: [ + '[{"role": "assistant", "parts": [{"type": "text", "content": "The weather in Paris is currently rainy with a temperature of 57°F."}], "finish_reason": "stop"}]', + '[{"answers": {"authIssue": {"type": "noul", "noul": 0.98}, "department": {"type": "choice", "choice": "billing", "confidence": 0.28, "probabilities": {"billing": 0.64, "technical": 0.36}}}}]', + '[{"answers": {"invoice_number": {"type": "text", "p_null": 3.8e-10, "span": [8, 16], "guess": "INV-2291", "confidence": 1, "value": "INV-2291"}, "due_date": {"type": "date", "p_null": 1.9e-08, "guess": "2026-10-02", "confidence": 0.9999, "value": "2026-10-02"}, "total": {"type": "number", "p_null": 2.3e-08, "guess": 1080, "confidence": 0.9999, "value": 1080}, "contacts": {"type": "list", "value": ["Dana Ruiz", "Priya Shah"], "spans": [[120, 129], [131, 141]], "confidence": 0.9999}, "po_number": {"type": "text", "p_null": 1, "span": [8, 16], "guess": "INV-2291", "confidence": 0.9995, "value": null}, "days_to_pay": {"type": "estimate", "value": 30, "mean": 29.535, "median": 30, "p10": 21, "p90": 38, "p_below": 5.9e-06, "p_above": 1.2e-05}}, "truncated": false}]', + ], aliases: ['ai.response.toolCalls', 'ai.response.text'], changelog: [ + { + version: 'next', + prs: [728], + description: + 'Describe the extraction message shape for gen_ai.extract and move operation-specific shapes to additional context', + }, { version: '0.26.0', prs: [650], description: 'Describe the evaluation message shape for gen_ai.evaluate' }, { version: '0.4.0', prs: [221] }, ], + additionalContext: [ + 'For `gen_ai.decide` operations, the array holds one object `{answers: {...}}`. `answers` holds the answers keyed by question name, as the provider returned them. The second example shows this shape.', + 'For `gen_ai.extract` operations, the array holds one object `{answers: {: }, truncated: boolean, state_text?: string}`. This object is the response body as the provider returned it, without `model` and `usage`. Do not change the values. `answers` holds one answer for each field in the input, keyed by field name. A field that the model cannot determine has a `value` of `null`. `span` and `spans` are `[start, end]` character offsets into `state` (or into `state_text`, if the provider returns it), and `end` is exclusive. Set the model in `gen_ai.response.model` and the token counts in `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens`. The third example shows this shape.', + ], }, 'gen_ai.pipeline.name': { brief: 'Name of the AI pipeline or chain being executed.', diff --git a/javascript/sentry-conventions/src/op.ts b/javascript/sentry-conventions/src/op.ts index b23bab62..949ad1d6 100644 --- a/javascript/sentry-conventions/src/op.ts +++ b/javascript/sentry-conventions/src/op.ts @@ -349,6 +349,11 @@ export const GEN_AI_RERANK = 'gen_ai.rerank'; */ export const GEN_AI_DECIDE = 'gen_ai.decide'; +/** + * Extraction of a set of typed fields (e.g., text, date, number, list, or estimate) from a shared state by a generative AI model + */ +export const GEN_AI_EXTRACT = 'gen_ai.extract'; + /** * Evaluation of typed questions (e.g., boolean, choice, or score) about a shared state by a generative AI model * diff --git a/javascript/sentry-conventions/src/search.ts b/javascript/sentry-conventions/src/search.ts index 8348fdd4..8f4b0e54 100644 --- a/javascript/sentry-conventions/src/search.ts +++ b/javascript/sentry-conventions/src/search.ts @@ -8576,7 +8576,7 @@ export const ATTRIBUTE_SEARCH_METADATA: Record canonicalName: 'gen_ai.input.messages', type: 'string', brief: - 'The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", state: ..., questions: {...}}` with the evaluated state and the questions keyed by name, as the caller passed them.', + 'The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. The first example shows this shape. Some operations use a different shape. See the additional context.', deprecationChain: [ 'gen_ai.input.messages', 'ai.input_messages', @@ -8639,7 +8639,7 @@ export const ATTRIBUTE_SEARCH_METADATA: Record canonicalName: 'gen_ai.output.messages', type: 'string', brief: - 'The model\'s response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", answers: {...}}` with the answers keyed by question name, as the provider returned them.', + "The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. The first example shows this shape. Some operations use a different shape. See the additional context.", deprecationChain: [ 'gen_ai.output.messages', 'ai.response.text', diff --git a/model/attributes/gen_ai/gen_ai__input__messages.json b/model/attributes/gen_ai/gen_ai__input__messages.json index b67370ef..d6957ff5 100644 --- a/model/attributes/gen_ai/gen_ai__input__messages.json +++ b/model/attributes/gen_ai/gen_ai__input__messages.json @@ -1,15 +1,28 @@ { "key": "gen_ai.input.messages", - "brief": "The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `\"user\"`, `\"assistant\"`, `\"tool\"`, or `\"system\"`. For messages of the role `\"tool\"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: \"text\", text:\"...\"}`. For `gen_ai.decide` operations, the array holds one object `{type: \"evaluation\", state: ..., questions: {...}}` with the evaluated state and the questions keyed by name, as the caller passed them.", + "brief": "The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `\"user\"`, `\"assistant\"`, `\"tool\"`, or `\"system\"`. For messages of the role `\"tool\"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: \"text\", text:\"...\"}`. The first example shows this shape. Some operations use a different shape. See the additional context.", "type": "string", "apply_scrubbing": { "key": "manual" }, "is_in_otel": true, - "example": "[{\"role\": \"user\", \"parts\": [{\"type\": \"text\", \"content\": \"Weather in Paris?\"}]}, {\"role\": \"assistant\", \"parts\": [{\"type\": \"tool_call\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]}, {\"role\": \"tool\", \"parts\": [{\"type\": \"tool_call_response\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"result\": \"rainy, 57°F\"}]}]", + "examples": [ + "[{\"role\": \"user\", \"parts\": [{\"type\": \"text\", \"content\": \"Weather in Paris?\"}]}, {\"role\": \"assistant\", \"parts\": [{\"type\": \"tool_call\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]}, {\"role\": \"tool\", \"parts\": [{\"type\": \"tool_call_response\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"result\": \"rainy, 57°F\"}]}]", + "[{\"state\": \"I cannot log in, and I also want a refund for last month.\", \"questions\": {\"authIssue\": {\"type\": \"noul\", \"instructions\": \"Is there a login problem?\"}, \"department\": {\"type\": \"choice\", \"instructions\": \"Which team should handle this?\", \"criteria\": {\"billing\": \"Charges and refunds\", \"technical\": \"Bugs and outages\"}}}}]", + "[{\"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\"}, \"po_number\": {\"type\": \"text\", \"instructions\": \"Purchase order number\"}, \"days_to_pay\": {\"type\": \"estimate\", \"min\": 0, \"max\": 120, \"step\": 1, \"instructions\": \"Days after issue until this invoice is paid\"}}}]" + ], "alias": ["ai.texts", "ai.prompt.messages", "gen_ai.prompt", "ai.prompt"], "visibility": "public", + "additional_context": [ + "For `gen_ai.decide` operations, the array holds one object `{state: ..., questions: {...}}`. `state` is the evaluated state. `questions` holds the questions keyed by name, as the caller passed them. Replace binary data in `state`, including `data:` URLs, with the string `\"[Blob substitute]\"`. The second example shows this shape.", + "For `gen_ai.extract` operations, the array holds one object `{state?: string, fields: {: }, images?: string[], reference_date?: string, options?: {...}}`. This object is the request body as the caller passed it, without `model`. Do not change the values, and leave out the keys that the caller did not set. Do not record the image data. Replace each image with the string `\"[Blob substitute]\"`. `state` is the text that the model reads. `fields` holds the field specs keyed by field name. Set the model in `gen_ai.request.model`. The third example shows this shape." + ], "changelog": [ + { + "version": "next", + "prs": [728], + "description": "Describe the extraction message shape for gen_ai.extract and move operation-specific shapes to additional context" + }, { "version": "0.26.0", "prs": [650], diff --git a/model/attributes/gen_ai/gen_ai__operation__name.json b/model/attributes/gen_ai/gen_ai__operation__name.json index 93909997..21ba30a3 100644 --- a/model/attributes/gen_ai/gen_ai__operation__name.json +++ b/model/attributes/gen_ai/gen_ai__operation__name.json @@ -38,6 +38,9 @@ { "value": "execute_tool" }, + { + "value": "extract" + }, { "value": "generate_content" }, @@ -59,6 +62,11 @@ ], "visibility": "public", "changelog": [ + { + "version": "next", + "prs": [728], + "description": "Added 'extract' to the well-known values" + }, { "version": "0.27.0", "prs": [653, 680], diff --git a/model/attributes/gen_ai/gen_ai__output__messages.json b/model/attributes/gen_ai/gen_ai__output__messages.json index 010d97d9..6c6e2887 100644 --- a/model/attributes/gen_ai/gen_ai__output__messages.json +++ b/model/attributes/gen_ai/gen_ai__output__messages.json @@ -1,15 +1,28 @@ { "key": "gen_ai.output.messages", - "brief": "The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. For `gen_ai.decide` operations, the array holds one object `{type: \"evaluation\", answers: {...}}` with the answers keyed by question name, as the provider returned them.", + "brief": "The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. The first example shows this shape. Some operations use a different shape. See the additional context.", "type": "string", "apply_scrubbing": { "key": "manual" }, "is_in_otel": true, - "example": "[{\"role\": \"assistant\", \"parts\": [{\"type\": \"text\", \"content\": \"The weather in Paris is currently rainy with a temperature of 57°F.\"}], \"finish_reason\": \"stop\"}]", + "examples": [ + "[{\"role\": \"assistant\", \"parts\": [{\"type\": \"text\", \"content\": \"The weather in Paris is currently rainy with a temperature of 57°F.\"}], \"finish_reason\": \"stop\"}]", + "[{\"answers\": {\"authIssue\": {\"type\": \"noul\", \"noul\": 0.98}, \"department\": {\"type\": \"choice\", \"choice\": \"billing\", \"confidence\": 0.28, \"probabilities\": {\"billing\": 0.64, \"technical\": 0.36}}}}]", + "[{\"answers\": {\"invoice_number\": {\"type\": \"text\", \"p_null\": 3.8e-10, \"span\": [8, 16], \"guess\": \"INV-2291\", \"confidence\": 1, \"value\": \"INV-2291\"}, \"due_date\": {\"type\": \"date\", \"p_null\": 1.9e-08, \"guess\": \"2026-10-02\", \"confidence\": 0.9999, \"value\": \"2026-10-02\"}, \"total\": {\"type\": \"number\", \"p_null\": 2.3e-08, \"guess\": 1080, \"confidence\": 0.9999, \"value\": 1080}, \"contacts\": {\"type\": \"list\", \"value\": [\"Dana Ruiz\", \"Priya Shah\"], \"spans\": [[120, 129], [131, 141]], \"confidence\": 0.9999}, \"po_number\": {\"type\": \"text\", \"p_null\": 1, \"span\": [8, 16], \"guess\": \"INV-2291\", \"confidence\": 0.9995, \"value\": null}, \"days_to_pay\": {\"type\": \"estimate\", \"value\": 30, \"mean\": 29.535, \"median\": 30, \"p10\": 21, \"p90\": 38, \"p_below\": 5.9e-06, \"p_above\": 1.2e-05}}, \"truncated\": false}]" + ], "alias": ["ai.response.toolCalls", "ai.response.text"], "visibility": "public", + "additional_context": [ + "For `gen_ai.decide` operations, the array holds one object `{answers: {...}}`. `answers` holds the answers keyed by question name, as the provider returned them. The second example shows this shape.", + "For `gen_ai.extract` operations, the array holds one object `{answers: {: }, truncated: boolean, state_text?: string}`. This object is the response body as the provider returned it, without `model` and `usage`. Do not change the values. `answers` holds one answer for each field in the input, keyed by field name. A field that the model cannot determine has a `value` of `null`. `span` and `spans` are `[start, end]` character offsets into `state` (or into `state_text`, if the provider returns it), and `end` is exclusive. Set the model in `gen_ai.response.model` and the token counts in `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens`. The third example shows this shape." + ], "changelog": [ + { + "version": "next", + "prs": [728], + "description": "Describe the extraction message shape for gen_ai.extract and move operation-specific shapes to additional context" + }, { "version": "0.26.0", "prs": [650], diff --git a/model/description/gen_ai.json b/model/description/gen_ai.json index 276d0c1d..3c61c509 100644 --- a/model/description/gen_ai.json +++ b/model/description/gen_ai.json @@ -25,6 +25,7 @@ "gen_ai.chat", "gen_ai.decide", "gen_ai.embeddings", + "gen_ai.extract", "gen_ai.generate_content", "gen_ai.rerank", "gen_ai.responses", @@ -38,6 +39,7 @@ "generate_content gemini-2.0-flash-001", "embeddings text-embedding-3-small", "decide jev-latest", + "extract clairic", "chat" ] }, diff --git a/model/name/gen_ai.json b/model/name/gen_ai.json index e998fa88..149bdf09 100644 --- a/model/name/gen_ai.json +++ b/model/name/gen_ai.json @@ -32,6 +32,7 @@ "gen_ai.chat", "gen_ai.decide", "gen_ai.embeddings", + "gen_ai.extract", "gen_ai.execute_tool", "gen_ai.generate_content", "gen_ai.rerank", @@ -50,6 +51,7 @@ "chat claude-opus-4", "execute_tool get_weather", "decide jev-latest", + "extract clairic", "chat" ] }, diff --git a/model/op/gen_ai.json b/model/op/gen_ai.json index 16c81f32..9804bcc3 100644 --- a/model/op/gen_ai.json +++ b/model/op/gen_ai.json @@ -34,6 +34,10 @@ "name": "gen_ai.decide", "description": "Decision on a set of typed questions (e.g., boolean, choice, or score) about a shared state by a generative AI model" }, + { + "name": "gen_ai.extract", + "description": "Extraction of a set of typed fields (e.g., text, date, number, list, or estimate) from a shared state by a generative AI model" + }, { "name": "gen_ai.evaluate", "description": "Evaluation of typed questions (e.g., boolean, choice, or score) about a shared state by a generative AI model", diff --git a/python/src/sentry_conventions/attributes.py b/python/src/sentry_conventions/attributes.py index be5f40f8..80f25850 100644 --- a/python/src/sentry_conventions/attributes.py +++ b/python/src/sentry_conventions/attributes.py @@ -4995,7 +4995,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): # Path: model/attributes/gen_ai/gen_ai__input__messages.json GEN_AI_INPUT_MESSAGES: Literal["gen_ai.input.messages"] = "gen_ai.input.messages" - """The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", state: ..., questions: {...}}` with the evaluated state and the questions keyed by name, as the caller passed them. + """The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. The first example shows this shape. Some operations use a different shape. See the additional context. Type: str Apply Scrubbing: manual @@ -5003,6 +5003,8 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): Visibility: public Aliases: ai.texts, ai.prompt.messages, gen_ai.prompt, ai.prompt Example: "[{\"role\": \"user\", \"parts\": [{\"type\": \"text\", \"content\": \"Weather in Paris?\"}]}, {\"role\": \"assistant\", \"parts\": [{\"type\": \"tool_call\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]}, {\"role\": \"tool\", \"parts\": [{\"type\": \"tool_call_response\", \"id\": \"call_VSPygqKTWdrhaFErNvMV18Yl\", \"result\": \"rainy, 57°F\"}]}]" + Example: "[{\"state\": \"I cannot log in, and I also want a refund for last month.\", \"questions\": {\"authIssue\": {\"type\": \"noul\", \"instructions\": \"Is there a login problem?\"}, \"department\": {\"type\": \"choice\", \"instructions\": \"Which team should handle this?\", \"criteria\": {\"billing\": \"Charges and refunds\", \"technical\": \"Bugs and outages\"}}}}]" + Example: "[{\"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\"}, \"po_number\": {\"type\": \"text\", \"instructions\": \"Purchase order number\"}, \"days_to_pay\": {\"type\": \"estimate\", \"min\": 0, \"max\": 120, \"step\": 1, \"instructions\": \"Days after issue until this invoice is paid\"}}}]" """ # Path: model/attributes/gen_ai/gen_ai__memory__query__text.json @@ -5088,6 +5090,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): - `embeddings` - `evaluate` - `execute_tool` + - `extract` - `generate_content` - `invoke_agent` - `search_memory` @@ -5117,7 +5120,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): # Path: model/attributes/gen_ai/gen_ai__output__messages.json GEN_AI_OUTPUT_MESSAGES: Literal["gen_ai.output.messages"] = "gen_ai.output.messages" - """The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", answers: {...}}` with the answers keyed by question name, as the provider returned them. + """The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. The first example shows this shape. Some operations use a different shape. See the additional context. Type: str Apply Scrubbing: manual @@ -5125,6 +5128,8 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): Visibility: public Aliases: ai.response.toolCalls, ai.response.text Example: "[{\"role\": \"assistant\", \"parts\": [{\"type\": \"text\", \"content\": \"The weather in Paris is currently rainy with a temperature of 57°F.\"}], \"finish_reason\": \"stop\"}]" + Example: "[{\"answers\": {\"authIssue\": {\"type\": \"noul\", \"noul\": 0.98}, \"department\": {\"type\": \"choice\", \"choice\": \"billing\", \"confidence\": 0.28, \"probabilities\": {\"billing\": 0.64, \"technical\": 0.36}}}}]" + Example: "[{\"answers\": {\"invoice_number\": {\"type\": \"text\", \"p_null\": 3.8e-10, \"span\": [8, 16], \"guess\": \"INV-2291\", \"confidence\": 1, \"value\": \"INV-2291\"}, \"due_date\": {\"type\": \"date\", \"p_null\": 1.9e-08, \"guess\": \"2026-10-02\", \"confidence\": 0.9999, \"value\": \"2026-10-02\"}, \"total\": {\"type\": \"number\", \"p_null\": 2.3e-08, \"guess\": 1080, \"confidence\": 0.9999, \"value\": 1080}, \"contacts\": {\"type\": \"list\", \"value\": [\"Dana Ruiz\", \"Priya Shah\"], \"spans\": [[120, 129], [131, 141]], \"confidence\": 0.9999}, \"po_number\": {\"type\": \"text\", \"p_null\": 1, \"span\": [8, 16], \"guess\": \"INV-2291\", \"confidence\": 0.9995, \"value\": null}, \"days_to_pay\": {\"type\": \"estimate\", \"value\": 30, \"mean\": 29.535, \"median\": 30, \"p10\": 21, \"p90\": 38, \"p_below\": 5.9e-06, \"p_above\": 1.2e-05}}, \"truncated\": false}]" """ # Path: model/attributes/gen_ai/gen_ai__pipeline__name.json @@ -18880,7 +18885,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ], ), "gen_ai.input.messages": AttributeMetadata( - brief='The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", state: ..., questions: {...}}` with the evaluated state and the questions keyed by name, as the caller passed them.', + brief='The messages passed to the model. It has to be a stringified version of an array of objects. The `role` attribute of each object must be `"user"`, `"assistant"`, `"tool"`, or `"system"`. For messages of the role `"tool"`, the `content` can be a string or an arbitrary object with information about the tool call. For other messages the `content` can be either a string or a list of objects in the format `{type: "text", text:"..."}`. The first example shows this shape. Some operations use a different shape. See the additional context.', type=AttributeType.STRING, keys=( "gen_ai.input.messages", @@ -18894,8 +18899,18 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): is_in_otel=True, visibility=Visibility.PUBLIC, example='[{"role": "user", "parts": [{"type": "text", "content": "Weather in Paris?"}]}, {"role": "assistant", "parts": [{"type": "tool_call", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "name": "get_weather", "arguments": {"location": "Paris"}}]}, {"role": "tool", "parts": [{"type": "tool_call_response", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "result": "rainy, 57°F"}]}]', + examples=[ + '[{"role": "user", "parts": [{"type": "text", "content": "Weather in Paris?"}]}, {"role": "assistant", "parts": [{"type": "tool_call", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "name": "get_weather", "arguments": {"location": "Paris"}}]}, {"role": "tool", "parts": [{"type": "tool_call_response", "id": "call_VSPygqKTWdrhaFErNvMV18Yl", "result": "rainy, 57°F"}]}]', + '[{"state": "I cannot log in, and I also want a refund for last month.", "questions": {"authIssue": {"type": "noul", "instructions": "Is there a login problem?"}, "department": {"type": "choice", "instructions": "Which team should handle this?", "criteria": {"billing": "Charges and refunds", "technical": "Bugs and outages"}}}}]', + '[{"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"}, "po_number": {"type": "text", "instructions": "Purchase order number"}, "days_to_pay": {"type": "estimate", "min": 0, "max": 120, "step": 1, "instructions": "Days after issue until this invoice is paid"}}}]', + ], aliases=["ai.texts", "ai.prompt.messages", "gen_ai.prompt", "ai.prompt"], changelog=[ + ChangelogEntry( + version="next", + prs=[728], + description="Describe the extraction message shape for gen_ai.extract and move operation-specific shapes to additional context", + ), ChangelogEntry( version="0.26.0", prs=[650], @@ -18907,6 +18922,10 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ChangelogEntry(version="0.5.0", prs=[264]), ChangelogEntry(version="0.4.0", prs=[221]), ], + additional_context=[ + 'For `gen_ai.decide` operations, the array holds one object `{state: ..., questions: {...}}`. `state` is the evaluated state. `questions` holds the questions keyed by name, as the caller passed them. Replace binary data in `state`, including `data:` URLs, with the string `"[Blob substitute]"`. The second example shows this shape.', + 'For `gen_ai.extract` operations, the array holds one object `{state?: string, fields: {: }, images?: string[], reference_date?: string, options?: {...}}`. This object is the request body as the caller passed it, without `model`. Do not change the values, and leave out the keys that the caller did not set. Do not record the image data. Replace each image with the string `"[Blob substitute]"`. `state` is the text that the model reads. `fields` holds the field specs keyed by field name. Set the model in `gen_ai.request.model`. The third example shows this shape.', + ], ), "gen_ai.memory.query.text": AttributeMetadata( brief="The search query used to retrieve memories. Only applicable to 'search_memory'. Opt-in: instrumentations SHOULD NOT capture this by default and SHOULD gate it behind explicit user opt-in, as it may contain sensitive information.", @@ -19004,6 +19023,11 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): visibility=Visibility.PUBLIC, example="chat", changelog=[ + ChangelogEntry( + version="next", + prs=[728], + description="Added 'extract' to the well-known values", + ), ChangelogEntry( version="0.27.0", prs=[653, 680], @@ -19030,7 +19054,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ], ), "gen_ai.output.messages": AttributeMetadata( - brief='The model\'s response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. For `gen_ai.decide` operations, the array holds one object `{type: "evaluation", answers: {...}}` with the answers keyed by question name, as the provider returned them.', + brief="The model's response messages. It has to be a stringified version of an array of message objects, which can include text responses and tool calls. The first example shows this shape. Some operations use a different shape. See the additional context.", type=AttributeType.STRING, keys=( "gen_ai.output.messages", @@ -19043,8 +19067,18 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): is_in_otel=True, visibility=Visibility.PUBLIC, example='[{"role": "assistant", "parts": [{"type": "text", "content": "The weather in Paris is currently rainy with a temperature of 57°F."}], "finish_reason": "stop"}]', + examples=[ + '[{"role": "assistant", "parts": [{"type": "text", "content": "The weather in Paris is currently rainy with a temperature of 57°F."}], "finish_reason": "stop"}]', + '[{"answers": {"authIssue": {"type": "noul", "noul": 0.98}, "department": {"type": "choice", "choice": "billing", "confidence": 0.28, "probabilities": {"billing": 0.64, "technical": 0.36}}}}]', + '[{"answers": {"invoice_number": {"type": "text", "p_null": 3.8e-10, "span": [8, 16], "guess": "INV-2291", "confidence": 1, "value": "INV-2291"}, "due_date": {"type": "date", "p_null": 1.9e-08, "guess": "2026-10-02", "confidence": 0.9999, "value": "2026-10-02"}, "total": {"type": "number", "p_null": 2.3e-08, "guess": 1080, "confidence": 0.9999, "value": 1080}, "contacts": {"type": "list", "value": ["Dana Ruiz", "Priya Shah"], "spans": [[120, 129], [131, 141]], "confidence": 0.9999}, "po_number": {"type": "text", "p_null": 1, "span": [8, 16], "guess": "INV-2291", "confidence": 0.9995, "value": null}, "days_to_pay": {"type": "estimate", "value": 30, "mean": 29.535, "median": 30, "p10": 21, "p90": 38, "p_below": 5.9e-06, "p_above": 1.2e-05}}, "truncated": false}]', + ], aliases=["ai.response.toolCalls", "ai.response.text"], changelog=[ + ChangelogEntry( + version="next", + prs=[728], + description="Describe the extraction message shape for gen_ai.extract and move operation-specific shapes to additional context", + ), ChangelogEntry( version="0.26.0", prs=[650], @@ -19052,6 +19086,10 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ), ChangelogEntry(version="0.4.0", prs=[221]), ], + additional_context=[ + "For `gen_ai.decide` operations, the array holds one object `{answers: {...}}`. `answers` holds the answers keyed by question name, as the provider returned them. The second example shows this shape.", + "For `gen_ai.extract` operations, the array holds one object `{answers: {: }, truncated: boolean, state_text?: string}`. This object is the response body as the provider returned it, without `model` and `usage`. Do not change the values. `answers` holds one answer for each field in the input, keyed by field name. A field that the model cannot determine has a `value` of `null`. `span` and `spans` are `[start, end]` character offsets into `state` (or into `state_text`, if the provider returns it), and `end` is exclusive. Set the model in `gen_ai.response.model` and the token counts in `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens`. The third example shows this shape.", + ], ), "gen_ai.pipeline.name": AttributeMetadata( brief="Name of the AI pipeline or chain being executed.", diff --git a/rust/src/op.rs b/rust/src/op.rs index 83aa0f8a..de98ddec 100644 --- a/rust/src/op.rs +++ b/rust/src/op.rs @@ -227,6 +227,9 @@ pub const GEN_AI_RERANK: &str = "gen_ai.rerank"; /// Decision on a set of typed questions (e.g., boolean, choice, or score) about a shared state by a generative AI model pub const GEN_AI_DECIDE: &str = "gen_ai.decide"; +/// Extraction of a set of typed fields (e.g., text, date, number, list, or estimate) from a shared state by a generative AI model +pub const GEN_AI_EXTRACT: &str = "gen_ai.extract"; + /// Evaluation of typed questions (e.g., boolean, choice, or score) about a shared state by a generative AI model #[deprecated(note = "Use `GEN_AI_DECIDE` (gen_ai.decide) instead")] pub const GEN_AI_EVALUATE: &str = "gen_ai.evaluate";