Skip to content

feat(eap): Implement gen_ai attribute transformations#6201

Open
constantinius wants to merge 1 commit into
masterfrom
constantinius/feat/eap/gen-ai-attribute-transformations
Open

feat(eap): Implement gen_ai attribute transformations#6201
constantinius wants to merge 1 commit into
masterfrom
constantinius/feat/eap/gen-ai-attribute-transformations

Conversation

@constantinius

@constantinius constantinius commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the two gen_ai attribute transformations introduced in sentry-conventions PR #465.

Old SDKs send AI messages and responses using deprecated attributes (gen_ai.request.messages, gen_ai.response.text, gen_ai.response.tool_calls) whose value shapes differ from the canonical replacements (gen_ai.input.messages, gen_ai.output.messages). Simple key renaming is not enough — the values need reshaping to match the new parts-based message schema.

These transformations run inside AI span normalization on both the SpanV1 (enrich_ai_span_data) and SpanV2 (normalize_ai) paths, using the AttributesLike abstraction. Attributes with the new "transform" deprecation status produce WriteBehavior::CurrentName so normalize_attribute_names leaves them alone.

Contributes to TET-2587

@linear-code

linear-code Bot commented Jul 9, 2026

Copy link
Copy Markdown

TET-2587

@constantinius
constantinius requested a review from a team July 10, 2026 09:51
@constantinius
constantinius marked this pull request as ready for review July 10, 2026 09:51
@constantinius
constantinius requested a review from a team as a code owner July 10, 2026 09:51
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs
@constantinius constantinius changed the title feat(eap): implement gen_ai attribute transformations feat(eap): Implement gen_ai attribute transformations Jul 10, 2026
Comment thread relay-server/src/processing/legacy_spans/store.rs Outdated
Comment thread relay-event-normalization/src/eap/ai.rs Outdated
Comment thread relay-server/src/processing/transactions/spans.rs Outdated
Comment thread relay-event-normalization/src/eap/ai.rs Outdated
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs
attributes.remove(GEN_AI__RESPONSE__TOOL_CALLS);

if parts.is_empty() {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response attrs dropped when empty

High Severity

When gen_ai.response.text and/or gen_ai.response.tool_calls are present but parsing yields no output parts (e.g. non-JSON tool calls like some_tool_calls, or JSON text with no usable content), the code still removes both deprecated keys and writes nothing to gen_ai.output.messages, so response data is lost instead of being moved or preserved like invalid gen_ai.request.messages.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit aa94a95. Configure here.

Comment thread relay-event-normalization/src/eap/ai.rs
Comment thread relay-event-schema/src/protocol/span.rs Outdated
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs
Comment thread relay-event-normalization/src/eap/ai.rs Outdated
@Dav1dde

Dav1dde commented Jul 13, 2026

Copy link
Copy Markdown
Member

@constantinius talked with the team, we'll look into dealing with the transaction normalization case in #6216 - Since you already removed the code for it, is this normalization required to run on transaction spans?

So to get you unblocked I would propose we merge this change for the span streaming pipeline and we as a team take over the transaction change, if it is necessary.

Also please remove the redundant information from the PR description, changed files and tests I can tell by the diff, even the functional changes I can see in code, the important information in the description and later commit is the motivation and the effect not the functional changes, which are already present in code.

@loewenheim loewenheim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good start. Please feel free to ask if you need help with the requested changes.

Comment thread relay-conventions/build/attributes.rs Outdated
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs Outdated
Comment thread tests/integration/test_ai.py
Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary anymore, master has a more up to date version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentry-conventions PR contains the transformations. We should point to that version, once its merged.

@constantinius
constantinius force-pushed the constantinius/feat/eap/gen-ai-attribute-transformations branch from 4b56bc5 to 5b1564d Compare July 16, 2026 08:11
Implement the two attribute transformations from sentry-conventions
PR #465: `gen_ai_request_messages_to_input_messages` and
`gen_ai_response_to_output_messages`.

These reshape attribute values beyond simple key renaming.
`gen_ai.request.messages` with `content` fields is converted to
`gen_ai.input.messages` with `parts` arrays. `gen_ai.response.text`
(plain string, JSON string array, or objects with content) and
`gen_ai.response.tool_calls` are combined into a single
`gen_ai.output.messages` assistant message with typed parts.

The transformation is generic over `AttributesLike` so it works on both
`Attributes` (SpanV2) and `SpanData` (SpanV1) without duplication. It
runs inside both `normalize_ai` (EAP pipeline) and `enrich_ai_span_data`
(transaction/legacy pipeline).

Attributes with the new `"transform"` deprecation status produce
`WriteBehavior::CurrentName` so `normalize_attribute_names` leaves them
alone — the dedicated transformation code handles the full
move-and-reshape. Deprecated keys are always cleaned up, even when the
canonical key already exists or the value cannot be parsed.

Contributes to TET-2587
@constantinius
constantinius force-pushed the constantinius/feat/eap/gen-ai-attribute-transformations branch from 5b1564d to 8f8a074 Compare July 16, 2026 08:39

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8f8a074. Configure here.

Comment thread relay-event-normalization/src/eap/gen_ai_transform.rs

@loewenheim loewenheim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot better. I have two comments, otherwise I'm good with merging this. If you like I can also take care of these.

/// A message in the old `gen_ai.request.messages` format.
#[derive(Deserialize)]
struct OldMessage {
#[serde(default)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessary for Option.

Suggested change
#[serde(default)]

// and let the rest carry the original fields.
return NewMessage {
role: None,
parts: vec![],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parts doesn't have skip_serializing_if. That means that in this case it will be serialized as parts: []. Not sure if that is intentional.

Comment thread tests/integration/test_ai.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants