fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(core): Preserve OpenAI APIPromise methods like withResponse() - #18402

Closed
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise
Closed

fix(core): Preserve OpenAI APIPromise methods like withResponse()#18402
Sai-Tom wants to merge 5 commits into
getsentry:developfrom
Sai-Tom:fix/openai-preserve-apipromise

Conversation

@Sai-Tom

Copy link
Copy Markdown

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.
Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
Comment threadpackages/core/test/tracing/openai-integration-functions.test.ts Outdated
 - Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior"
Comment threadpackages/core/src/tracing/openai/index.ts Outdated
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods
- Add try-catch for synchronous exceptions
- Use .finally() to ensure span always ends even if attribute processing throws
- Add tests for error handling (sync throw + async reject)
- Update tests to match real OpenAI SDK behavior
Comment on lines +202 to +210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

Comment threadpackages/core/src/tracing/openai/index.ts Outdated
@RulaKhaledRulaKhaled self-assigned this Dec 9, 2025

@RulaKhaledRulaKhaled left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

@RulaKhaled

Copy link
Copy Markdown
Collaborator

Closing in favor of #19122

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

@Sai-Tom@RulaKhaled