Skip to content

feat(node): Add streaming to orchestrion OpenAI integration#21886

Merged
nicohrubec merged 1 commit into
nh/openai-orchestrionfrom
nh/openai-orchestrion-streaming
Jul 1, 2026
Merged

feat(node): Add streaming to orchestrion OpenAI integration#21886
nicohrubec merged 1 commit into
nh/openai-orchestrionfrom
nh/openai-orchestrion-streaming

Conversation

@nicohrubec

Copy link
Copy Markdown
Member

Stacked on #21877 (base: nh/openai-orchestrion) — review/merge that first.

Adds streaming (stream: true) support for chat.completions.create and responses.create in the orchestrion OpenAI integration.

A streaming create() resolves to a Stream the caller consumes later, so the span must outlive the channel event and be enriched from the accumulated chunks. We can't swap what create returns, but the Stream in ctx.result is the same instance the caller gets and asyncEnd fires before the caller iterates — so we patch its async iterator in place to run through the reused instrumentStream (which accumulates tokens/finish-reasons/text and ends the span when iteration completes), via bindTracingChannelToSpan's deferSpanEnd. Non-streaming and errored results keep the existing path.

Changes:

  • packages/core: export instrumentStream (pure generator, browser-safe).
  • integrations/tracing-channel/openai.ts: drop the streaming opt-out; add deferSpanEnd that patches the stream instance and hands span-ending to instrumentStream.
  • Extend the orchestrion test (streaming chat + responses spans, streamed response text under PII).

🤖 Generated with Claude Code

const instrumented = instrumentStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);
(result as { [Symbol.asyncIterator]: () => AsyncIterator<unknown> })[Symbol.asyncIterator] = () => instrumented;

return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Open streaming spans never end

Medium Severity

When wrapStreamResult defers span completion for stream: true, the span is only finished inside instrumentStream's finally block after async iteration. If the caller resolves create() but never drives the returned Stream (no for await, abandoned reference, or fire-and-forget), deferSpanEnd skips the normal span.end() path and the gen_ai span can remain open indefinitely.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 115b544. Configure here.

@nicohrubec nicohrubec force-pushed the nh/openai-orchestrion-streaming branch 2 times, most recently from 58d2833 to 345dcba Compare July 1, 2026 12:44
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.62 kB added added
@sentry/browser - with treeshaking flags 26.05 kB added added
@sentry/browser (incl. Tracing) 46.07 kB added added
@sentry/browser (incl. Tracing + Span Streaming) 47.82 kB added added
@sentry/browser (incl. Tracing, Profiling) 50.84 kB added added
@sentry/browser (incl. Tracing, Replay) 85.31 kB added added
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.91 kB added added
@sentry/browser (incl. Tracing, Replay with Canvas) 89.99 kB added added
@sentry/browser (incl. Tracing, Replay, Feedback) 102.67 kB added added
@sentry/browser (incl. Feedback) 44.8 kB added added
@sentry/browser (incl. sendFeedback) 32.42 kB added added
@sentry/browser (incl. FeedbackAsync) 37.55 kB added added
@sentry/browser (incl. Metrics) 28.68 kB added added
@sentry/browser (incl. Logs) 28.93 kB added added
@sentry/browser (incl. Metrics & Logs) 29.61 kB added added
@sentry/react 29.41 kB added added
@sentry/react (incl. Tracing) 48.38 kB added added
@sentry/vue 32.85 kB added added
@sentry/vue (incl. Tracing) 47.93 kB added added
@sentry/svelte 27.64 kB added added
CDN Bundle 30.02 kB added added
CDN Bundle (incl. Tracing) 48.02 kB added added
CDN Bundle (incl. Logs, Metrics) 31.58 kB added added
CDN Bundle (incl. Tracing, Logs, Metrics) 49.35 kB added added
CDN Bundle (incl. Replay, Logs, Metrics) 70.79 kB added added
CDN Bundle (incl. Tracing, Replay) 85.51 kB added added
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.79 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback) 91.32 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.56 kB added added
CDN Bundle - uncompressed 89.42 kB added added
CDN Bundle (incl. Tracing) - uncompressed 145.35 kB added added
CDN Bundle (incl. Logs, Metrics) - uncompressed 94.12 kB added added
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 149.32 kB added added
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.66 kB added added
CDN Bundle (incl. Tracing, Replay) - uncompressed 264.36 kB added added
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 268.32 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 278.06 kB added added
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 282.01 kB added added
@sentry/nextjs (client) 50.76 kB added added
@sentry/sveltekit (client) 46.46 kB added added
@sentry/core/server 77.8 kB added added
@sentry/core/browser 64.1 kB added added
@sentry/node-core 61.47 kB added added
@sentry/node 122.07 kB added added
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.95 kB added added
@sentry/node/light 50.45 kB added added
@sentry/node - without tracing 73.2 kB added added
@sentry/aws-serverless 84.09 kB added added
@sentry/cloudflare (withSentry) - minified 180.62 kB added added
@sentry/cloudflare (withSentry) 446.93 kB added added

Instruments streaming `chat.completions.create` / `responses.create` by patching
the returned `Stream`'s async iterator in place (via `bindTracingChannelToSpan`'s
`deferSpanEnd`) so the reused `instrumentStream` accumulates chunk state and ends
the span when iteration completes. Exports `instrumentStream` from core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nicohrubec nicohrubec force-pushed the nh/openai-orchestrion-streaming branch from 345dcba to 9ca6a54 Compare July 1, 2026 13:28
@nicohrubec nicohrubec merged commit 9ca6a54 into nh/openai-orchestrion Jul 1, 2026
10 checks passed
@nicohrubec nicohrubec deleted the nh/openai-orchestrion-streaming branch July 1, 2026 13:30

@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 9ca6a54. Configure here.

const instrumented = instrumentStream({ [Symbol.asyncIterator]: iterate }, span, recordOutputs ?? false);
result[Symbol.asyncIterator] = () => instrumented;

return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stream errors leave span ok

Medium Severity

When deferSpanEnd hands off a streaming result, wrapStreamResult never uses the provided end callback. If async iteration fails (network abort, parse error, etc.), instrumentStream still runs endStreamSpan in its finally block and ends the span without error status, and the integration’s captureError / error annotation path is skipped—unlike rejected create() calls or the mysql orchestrion deferSpanEnd pattern.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ca6a54. Configure here.

@nicohrubec

Copy link
Copy Markdown
Member Author

Folded directly into #21877 (fast-forwarded onto its base) — decided to ship the full OpenAI orchestrion surface in one PR.

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.

1 participant