Skip to content

feat(node): Instrument stream responses for openai #17110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from

Conversation

RulaKhaled
Copy link
Member

@RulaKhaled RulaKhaled commented Jul 21, 2025

This adds support for OpenAI streaming responses in the Node.js SDK

What's new

  • Streaming Chat Completions: Support for stream: true in chat.completions.create()
  • Streaming Responses API: Support for streaming in the new OpenAI Responses API

// Streaming chat completions - automatically instrumented
const stream = await openai.chat.completions.create({
  model: 'gpt-4',
  messages: [...],
  stream: true
});

// Streaming responses API - automatically instrumented  
const responseStream = await openai.responses.create({
  model: 'gpt-4',
  input: 'Hello',
  stream: true
});

Copy link
Contributor

github-actions bot commented Jul 21, 2025

size-limit report 📦

Path Size % Change Change
@sentry/browser 23.76 kB - -
@sentry/browser - with treeshaking flags 22.35 kB - -
@sentry/browser (incl. Tracing) 39.41 kB - -
@sentry/browser (incl. Tracing, Replay) 77.52 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 67.39 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 82.23 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 94.35 kB - -
@sentry/browser (incl. Feedback) 40.45 kB - -
@sentry/browser (incl. sendFeedback) 28.44 kB - -
@sentry/browser (incl. FeedbackAsync) 33.34 kB - -
@sentry/react 25.5 kB - -
@sentry/react (incl. Tracing) 41.38 kB - -
@sentry/vue 28.2 kB - -
@sentry/vue (incl. Tracing) 41.21 kB - -
@sentry/svelte 23.79 kB - -
CDN Bundle 25.28 kB - -
CDN Bundle (incl. Tracing) 39.28 kB - -
CDN Bundle (incl. Tracing, Replay) 75.39 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 80.82 kB - -
CDN Bundle - uncompressed 73.86 kB - -
CDN Bundle (incl. Tracing) - uncompressed 116.29 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 230.53 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 243.35 kB - -
@sentry/nextjs (client) 43.44 kB - -
@sentry/sveltekit (client) 39.84 kB - -
@sentry/node-core 47.48 kB - -
@sentry/node 144.9 kB +0.54% +775 B 🔺
@sentry/node - without tracing 91.58 kB - -
@sentry/aws-serverless 103.03 kB - -

View base workflow run

@RulaKhaled RulaKhaled changed the title [WIP] Stream responses OpenAI feat(node): Instrument stream responses for openai Jul 23, 2025
This reverts commit 1607304.
@RulaKhaled RulaKhaled requested a review from andreiborza July 24, 2025 12:43
@RulaKhaled RulaKhaled marked this pull request as ready for review July 24, 2025 14:24
@mydea mydea requested a review from AbhiPrasad July 25, 2025 09:24
Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

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

I'd like us to add some more jsdoc explanations to packages/core/src/utils/openai/streaming.ts

return;
}
if (streamEvent instanceof Error) {
captureException(streamEvent);
Copy link
Member

Choose a reason for hiding this comment

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

m: when we capture an exception here, should we also set span status to errored?

also, I think we need to mark this as unhandled.

Copy link
Member Author

Choose a reason for hiding this comment

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

Potentially yeah. It's a series of events we're capturing in a single span, which makes things a bit vague. I guess if one fails it's fair to assume the span is in an error state, will update

setTokenUsageAttributes,
} from './utils';

interface StreamingState {
Copy link
Member

Choose a reason for hiding this comment

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

m: It would be nice to have some doc strings about this interface and it's fields.

l: I'm not the biggest fan of having a big interface with a bunch of nullable fields. Can we type this a bit stronger? For example if chunk.usage we should always have promptTokens, completionTokens, and totalTokens defined. Having some helpers that construct state from input (builder pattern?) might also be a useful abstraction.

span.setAttributes({
[OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE]: new Date(timestamp * 1000).toISOString(),
});
}
Copy link

Choose a reason for hiding this comment

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

Bug: Function Change Introduces Invalid Default Attributes

The setCommonResponseAttributes function was modified to use required parameters, removing the original conditional checks that only set attributes if values were truthy. This change causes the function to always set span attributes, even when provided with empty strings or zero values from uninitialized streaming state. Consequently, spans will now include meaningless attributes such as empty strings for IDs/models and '1970-01-01T00:00:00.000Z' for timestamps, altering telemetry by setting invalid defaults instead of omitting these attributes.

Locations (1)

Fix in CursorFix in Web

@@ -195,6 +142,9 @@ function addRequestAttributes(span: Span, params: Record<string, unknown>): void
if ('input' in params) {
span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.input) });
}
if ('stream' in params) {
span.setAttributes({ [GEN_AI_REQUEST_STREAM_ATTRIBUTE]: Boolean(params.stream) });
}
Copy link

Choose a reason for hiding this comment

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

Bug: Attribute Duplication Causes Type Inconsistency

The GEN_AI_REQUEST_STREAM_ATTRIBUTE is set twice: once in extractRequestAttributes with the original params.stream value, and again in addRequestAttributes as Boolean(params.stream). This duplication can lead to the attribute being overwritten with an inconsistent type (original vs. boolean).

Locations (1)

Fix in Cursor Fix in Web

description: 'chat error-model stream-response',
op: 'gen_ai.chat',
origin: 'manual',
status: 'ok',
Copy link

Choose a reason for hiding this comment

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

Bug: Streaming Tests Fail on Error Handling

The tests for OpenAI streaming chat completions incorrectly expect status: 'ok' for error handling spans. When model: 'error-model' is used, an error is thrown before the stream is created, and the span's status should be unknown_error. This is consistent with non-streaming error handling and the instrumentation's behavior of setting an error status. This applies to both PII and non-PII test cases.

Locations (2)

Fix in Cursor Fix in Web

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.

2 participants