Skip to content

ref(core): Do not emit spans for chats.create in google-genai - #19990

Merged
nicohrubec merged 12 commits into
developfrom
nh/no-chats-create-span
Apr 2, 2026
Merged

ref(core): Do not emit spans for chats.create in google-genai#19990
nicohrubec merged 12 commits into
developfrom
nh/no-chats-create-span

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Mar 26, 2026

Copy link
Copy Markdown
Member

We currently emit spans for the google-genai chats.create() API. I think this is basically useless because chats.create() doesn't represent an actual interaction with an LLM model, instead it just constructs a local chat object as a result that then further exposes methods like sendMessage() that represent actual LLM interactions. This PR removes the spans for chats.create().

Since this API is a special case where we actually need to proxy the return object instead of the method call itself we had some surrounding logic hardcoded to this method. To make this a bit more future proof and also more explicit we now add a proxyResultPath field to the method registry that allows to define this behavior in the method registry without needing any hardcoded logic. Another benefit this has is that the full logic in the createDeepProxy methods in all our client-proxy based AI integrations (google-genai, openai, anthropic) is now essentially the same so could potentially in the future be easily merged into a shared abstraction.

Limitation: We do"loose" some data by not emitting this create span anymore, because the way this API works is that the user defines certain parameters only on the chats.create() call and these are then subsequently used for each chat.sendMessage() call. The correct way would be to send this data as part of each chat.sendMessage() chat span. We can think about doing this as part of this PR or doing a follow up.

@github-actions

github-actionsBot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (core, node) Portable Express integration by isaacs in #19928
  • (deno) Add denoRuntimeMetricsIntegration by chargome in #20023
  • (deps) Bump @xmldom/xmldom from 0.8.3 to 0.8.12 by dependabot in #20066

Bug Fixes 🐛

  • (aws-serverless) Add timeout to _endSpan forceFlush to prevent Lambda hanging by logaretm in #20064
  • (cloudflare) Ensure every request instruments functions by JPeer264 in #20044
  • (gatsby) Fix errorHandler signature to match bundler-plugin-core API by JPeer264 in #20048

Internal Changes 🔧

Core

  • Do not emit spans for chats.create in google-genai by nicohrubec in #19990
  • Simplify addResponseAttributes in openai integration by nicohrubec in #20013
  • Extract shared endStreamSpan for AI integrations by nicohrubec in #20021
  • Remove provider-specific AI span attributes in favor of gen_ai attributes in sentry conventions by nicohrubec in #20011

Other

  • Update validate-pr workflow by stephanie-anderson in #20072
  • Remove unused tsconfig-template folder by mydea in #20067

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser25.65 kB+0.02%+5 B 🔺
@sentry/browser - with treeshaking flags24.14 kB+0.03%+5 B 🔺
@sentry/browser (incl. Tracing)42.16 kB+0.02%+7 B 🔺
@sentry/browser (incl. Tracing, Profiling)46.77 kB+0.02%+9 B 🔺
@sentry/browser (incl. Tracing, Replay)80.94 kB+0.01%+5 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags70.56 kB+0.01%+5 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)85.66 kB+0.01%+8 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)97.92 kB+0.01%+5 B 🔺
@sentry/browser (incl. Feedback)42.42 kB+0.02%+6 B 🔺
@sentry/browser (incl. sendFeedback)30.31 kB+0.02%+6 B 🔺
@sentry/browser (incl. FeedbackAsync)35.29 kB+0.02%+6 B 🔺
@sentry/browser (incl. Metrics)26.96 kB+0.03%+7 B 🔺
@sentry/browser (incl. Logs)27.11 kB+0.03%+7 B 🔺
@sentry/browser (incl. Metrics & Logs)27.78 kB+0.03%+7 B 🔺
@sentry/react27.41 kB+0.03%+6 B 🔺
@sentry/react (incl. Tracing)44.48 kB+0.02%+5 B 🔺
@sentry/vue30.08 kB+0.02%+5 B 🔺
@sentry/vue (incl. Tracing)44.05 kB+0.02%+8 B 🔺
@sentry/svelte25.67 kB+0.02%+5 B 🔺
CDN Bundle28.32 kB+0.03%+6 B 🔺
CDN Bundle (incl. Tracing)43.11 kB+0.02%+6 B 🔺
CDN Bundle (incl. Logs, Metrics)29.68 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)44.16 kB+0.02%+7 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)68.48 kB+0.01%+6 B 🔺
CDN Bundle (incl. Tracing, Replay)80.01 kB+0.01%+7 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.05 kB+0.01%+6 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)85.55 kB+0.01%+5 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)86.58 kB+0.01%+6 B 🔺
CDN Bundle - uncompressed82.68 kB+0.03%+22 B 🔺
CDN Bundle (incl. Tracing) - uncompressed127.83 kB+0.02%+22 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed86.83 kB+0.03%+22 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed131.24 kB+0.02%+22 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed209.81 kB+0.02%+22 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed244.7 kB+0.01%+22 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed248.1 kB+0.01%+22 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed257.62 kB+0.01%+22 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed261 kB+0.01%+22 B 🔺
@sentry/nextjs (client)46.9 kB+0.02%+7 B 🔺
@sentry/sveltekit (client)42.62 kB+0.02%+7 B 🔺
@sentry/node-core55.77 kB+0.04%+17 B 🔺
@sentry/node172.03 kB-0.42%-719 B 🔽
@sentry/node - without tracing96.05 kB+0.05%+40 B 🔺
@sentry/aws-serverless112.85 kB+0.07%+78 B 🔺

View base workflow run

@github-actions

github-actionsBot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline10,873-8,445+29%
GET With Sentry2,08219%1,728+20%
GET With Sentry (error only)7,69471%5,877+31%
POST Baseline1,296-1,169+11%
POST With Sentry64850%587+10%
POST With Sentry (error only)1,13888%1,038+10%
MYSQL Baseline3,510-3,122+12%
MYSQL With Sentry43412%445-2%
MYSQL With Sentry (error only)2,96484%2,542+17%

View base workflow run

@nicohrubecnicohrubec changed the title Nh/no chats create spanref(core): Do not emit spans for chats.create in google-genaiMar 26, 2026
@nicohrubec
nicohrubecforce-pushed the nh/no-chats-create-span branch from 042667f to cb797a1CompareMarch 26, 2026 14:44
@nicohrubec
nicohrubecforce-pushed the nh/no-chats-create-span branch from 16c5086 to 15e6af4CompareMarch 27, 2026 13:46
@nicohrubec
nicohrubec marked this pull request as ready for review March 27, 2026 14:01

@andreiborzaandreiborza left a comment

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.

LGTM, could you please create an issue for carrying over the data we lose by not creating these spans anymore?

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

@andreiborza issue: #20086

Comment threadpackages/core/src/tracing/ai/utils.ts

@cursorcursorBot 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.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Tests lack assertion for absence of removed span
    • Added explicit negative span assertions in both Node and Cloudflare Google GenAI tests to ensure a chat gemini-1.5-pro create span is not present.

Create PR

Or push these changes by commenting:

@cursor push e8acf088c3
Preview (e8acf088c3)
diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts--- a/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts+++ b/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts@@ -77,6 +77,13 @@
}),
]),
);
+ expect(transactionEvent.spans).not.toEqual(+ expect.arrayContaining([+ expect.objectContaining({+ description: 'chat gemini-1.5-pro create',+ }),+ ]),+ );
})
.start();
await runner.makeRequest('get', '/');
diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts b/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts--- a/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts+++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts@@ -29,6 +29,8 @@
cleanupChildProcesses();
});
+ const CHAT_CREATE_SPAN_DESCRIPTION = 'chat gemini-1.5-pro create';+
const EXPECTED_TRANSACTION_DEFAULT_PII_FALSE = {
transaction: 'main',
spans: expect.arrayContaining([
@@ -166,7 +168,18 @@
test('creates google genai related spans with sendDefaultPii: false', async () => {
await createRunner()
.ignore('event')
- .expect({ transaction: EXPECTED_TRANSACTION_DEFAULT_PII_FALSE })+ .expect({+ transaction: transactionEvent => {+ expect(transactionEvent).toMatchObject(EXPECTED_TRANSACTION_DEFAULT_PII_FALSE);+ expect(transactionEvent.spans).not.toEqual(+ expect.arrayContaining([+ expect.objectContaining({+ description: CHAT_CREATE_SPAN_DESCRIPTION,+ }),+ ]),+ );+ },+ })
.start()
.completed();
});
@@ -176,7 +189,18 @@
test('creates google genai related spans with sendDefaultPii: true', async () => {
await createRunner()
.ignore('event')
- .expect({ transaction: EXPECTED_TRANSACTION_DEFAULT_PII_TRUE })+ .expect({+ transaction: transactionEvent => {+ expect(transactionEvent).toMatchObject(EXPECTED_TRANSACTION_DEFAULT_PII_TRUE);+ expect(transactionEvent.spans).not.toEqual(+ expect.arrayContaining([+ expect.objectContaining({+ description: CHAT_CREATE_SPAN_DESCRIPTION,+ }),+ ]),+ );+ },+ })
.start()
.completed();
});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@nicohrubec
nicohrubec merged commit 4816963 into developApr 2, 2026
472 of 474 checks passed
@nicohrubec
nicohrubec deleted the nh/no-chats-create-span branch April 2, 2026 08:37
isaacs added a commit that referenced this pull request Aug 28, 2026
… chat spans (#23316)
`chats.create()` takes a `config` (temperature, topP, topK,
maxOutputTokens, frequencyPenalty, presencePenalty, tools,
systemInstruction) that `@google/genai` reuses for every
`chat.sendMessage()` and `chat.sendMessageStream()` call on that chat.
#19990 removed the `chats.create()` span, which was the only span
reporting those values, and nothing took over. Chat spans have carried
model and token counts but no request config since then.
Both google-genai instrumentation paths are fixed:
- `instrumentGoogleGenAIClient`, the client proxy, used by
`@sentry/cloudflare` and `@sentry/vercel-edge` and available for manual
wrapping.
- `googleGenAIIntegration`, the diagnostics-channel integration, which
is the default for Node, Bun, Deno, Astro, AWS Lambda and Google Cloud
Functions.
Only the first path was in the original report, but both build their
attributes from the per-message arguments alone, so both lost the same
data.
**Decisions**
**The config is read from the chat instance, not captured at create
time.** `@google/genai` stores `config` as a plain property on the
`Chat` object, and both paths already hold that object: the proxy passes
it as the instrumented method's `context`, and the channel path receives
it as `data.self`. `extractModel()` already recovers the model the same
way. Carrying the `chats.create()` arguments forward instead would have
meant threading state through `createDeepProxy`, which fixes only the
proxy path and moves that function away from its `openai` and
`anthropic-ai` counterparts that #19990 deliberately converged. The cost
is a dependency on an internal field name, which this file already
accepts for `model` / `modelVersion`.
**A per-message config replaces the chat config, it does not merge into
it.** The SDK resolves the request as `params.config ?? chat.config`, so
a message that carries its own config sends only that config. The span
mirrors that. Merging key by key would report a create-time
`maxOutputTokens` alongside a per-message `temperature` and describe a
request that was never sent.
**The chat `history` stays off the message spans.** The SDK does send
it, folded into `contents`, and the instance carries the whole
transcript, but repeating every past turn on every message span
duplicates what earlier spans already reported and grows without bound.
`gen_ai.request.messages` keeps just the message being sent.
**Non-chat calls are unaffected.** `models.generateContent` and
`models.embedContent` have no chat instance, so they resolve their
config from their own arguments exactly as before.
Fixes#20086
AI assistance (Claude, Anthropic) was used in developing this change.
The design, review and verification were done by the author.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: isaacs <i@izs.me>
Sign up for freeto 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.

2 participants

@nicohrubec@andreiborza