Skip to content

fix(openai): Attach response model with streamed Completions API#5557

Open
alexander-alderman-webb wants to merge 2 commits intowebb/openai/add-response-modelfrom
webb/openai/add-response-model-completions
Open

fix(openai): Attach response model with streamed Completions API#5557
alexander-alderman-webb wants to merge 2 commits intowebb/openai/add-response-modelfrom
webb/openai/add-response-model-completions

Conversation

@alexander-alderman-webb
Copy link
Contributor

Description

Issues

Reminders

@alexander-alderman-webb alexander-alderman-webb requested a review from a team as a code owner February 26, 2026 16:25
@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

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).


Bug Fixes 🐛

Openai

  • Attach response model with streamed Completions API by alexander-alderman-webb in #5557
  • Attach response model with streamed Responses API by alexander-alderman-webb in #5554
  • Avoid consuming iterables passed to the Completions API by alexander-alderman-webb in #5489
  • Avoid consuming iterables passed to the Embeddings API by alexander-alderman-webb in #5491

Other

  • (anthropic) Fix token accounting by shellmayr in #5490
  • (google-genai) Remove agent spans for simple requests by alexander-alderman-webb in #5443
  • (grpc) Read method from handler_call_details for grpcio >= 1.76 compat by yeung108 in #5521
  • (pydantic-ai) Adapt to missing ToolManager._call_tool by sentrivana in #5522
  • (utils) Use HEROKU_BUILD_COMMIT env var for default release by ericapisani in #5499

Documentation 📚

  • Add debugging advice by alexander-alderman-webb in #5517
  • New integration guide by alexander-alderman-webb in #5476

Internal Changes 🔧

Agents

  • Add security-review skill to agent configuration by ericapisani in #5498
  • Add sentry skills to be used by warden in CI reviews by ericapisani in #5485

Openai

  • Only handle streamed results when applicable by alexander-alderman-webb in #5553
  • Extract input in API-specific functions by alexander-alderman-webb in #5546
  • Separate output handling by alexander-alderman-webb in #5543

Openai Agents

  • Remove set_data_normalized for primitive attributes by alexander-alderman-webb in #5509
  • Expect new tool fields by alexander-alderman-webb in #5471

Other

  • (ai) Add configuration for dotagents by ericapisani in #5480
  • (anthropic) Remove set_data_normalized for primitive attributes by alexander-alderman-webb in #5504
  • (github) Add warden configuration by ericapisani in #5484
  • (pydantic-ai) Remove set_data_normalized for the gen_ai.response.model attribute by alexander-alderman-webb in #5512
  • (repo) Add .serena to .gitignore by ericapisani in #5464
  • 🤖 Update test matrix with new releases (02/24) by github-actions in #5524
  • 🤖 Update test matrix with new releases (02/23) by github-actions in #5503
  • 🤖 Update test matrix with new releases (02/19) by github-actions in #5483
  • 🤖 Update test matrix with new releases (02/18) by github-actions in #5475

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Codecov Results 📊

6 passed | Total: 6 | Pass Rate: 100% | Execution Time: 1.65s

📊 Comparison with Base Branch

Metric Change
Total Tests 📉 -1
Passed Tests 📉 -1
Failed Tests
Skipped Tests

All tests are passing successfully.

❌ Patch coverage is 0.00%. Project has 15378 uncovered lines.
✅ Project coverage is 22.66%. Comparing base (base) to head (head).

Files with missing lines (181)
File Patch % Lines
utils.py 36.07% ⚠️ 583 Missing and 54 partials
langchain.py 3.28% ⚠️ 590 Missing
openai.py 4.80% ⚠️ 575 Missing
utils.py 0.00% ⚠️ 474 Missing
tracing_utils.py 29.39% ⚠️ 418 Missing and 20 partials
scope.py 62.61% ⚠️ 307 Missing and 67 partials
__init__.py 5.08% ⚠️ 374 Missing
starlette.py 5.34% ⚠️ 337 Missing
transport.py 23.04% ⚠️ 304 Missing and 2 partials
client.py 53.39% ⚠️ 234 Missing and 63 partials
mcp.py 5.24% ⚠️ 253 Missing
anthropic.py 9.06% ⚠️ 251 Missing
transaction_profiler.py 35.52% ⚠️ 216 Missing and 10 partials
tracing.py 62.95% ⚠️ 176 Missing and 49 partials
__init__.py 3.91% ⚠️ 221 Missing
langgraph.py 5.29% ⚠️ 215 Missing
utils.py 16.08% ⚠️ 214 Missing
span_processor.py 0.00% ⚠️ 205 Missing
continuous_profiler.py 43.45% ⚠️ 177 Missing and 17 partials
strawberry.py 8.54% ⚠️ 182 Missing
__init__.py 6.01% ⚠️ 172 Missing
aws_lambda.py 16.50% ⚠️ 167 Missing
spark_driver.py 0.00% ⚠️ 166 Missing
huggingface_hub.py 8.89% ⚠️ 164 Missing
sanic.py 9.60% ⚠️ 160 Missing
aiohttp.py 10.84% ⚠️ 148 Missing
cloud_resource_context.py 0.00% ⚠️ 145 Missing
ai_client.py 0.00% ⚠️ 145 Missing
rust_tracing.py 0.00% ⚠️ 143 Missing
litellm.py 0.00% ⚠️ 138 Missing
litestar.py 9.59% ⚠️ 132 Missing
starlite.py 8.33% ⚠️ 132 Missing
envelope.py 40.82% ⚠️ 116 Missing and 11 partials
falcon.py 8.94% ⚠️ 112 Missing
flask.py 12.50% ⚠️ 112 Missing
spotlight.py 28.47% ⚠️ 103 Missing and 8 partials
asgi.py 20.71% ⚠️ 111 Missing
cohere.py 12.70% ⚠️ 110 Missing
dramatiq.py 0.00% ⚠️ 110 Missing
arq.py 9.92% ⚠️ 109 Missing
asgi.py 0.00% ⚠️ 109 Missing
hub.py 45.60% ⚠️ 105 Missing and 3 partials
logging.py 32.68% ⚠️ 103 Missing and 3 partials
pymongo.py 10.17% ⚠️ 106 Missing
beat.py 8.62% ⚠️ 106 Missing
caching.py 0.00% ⚠️ 106 Missing
utils.py 0.00% ⚠️ 103 Missing
templates.py 0.00% ⚠️ 100 Missing
asyncpg.py 11.61% ⚠️ 99 Missing
quart.py 16.10% ⚠️ 99 Missing
wsgi.py 22.66% ⚠️ 99 Missing
gcp.py 0.00% ⚠️ 98 Missing
otlp.py 0.00% ⚠️ 97 Missing
utils.py 13.39% ⚠️ 97 Missing
agent_run.py 0.00% ⚠️ 97 Missing
sessions.py 27.82% ⚠️ 96 Missing
models.py 4.95% ⚠️ 96 Missing
pyramid.py 13.76% ⚠️ 94 Missing
tornado.py 14.55% ⚠️ 94 Missing
__init__.py 0.00% ⚠️ 93 Missing
_wsgi_common.py 28.35% ⚠️ 91 Missing
bottle.py 11.65% ⚠️ 91 Missing
middleware.py 0.00% ⚠️ 90 Missing
agent_run.py 0.00% ⚠️ 90 Missing
stdlib.py 35.82% ⚠️ 86 Missing and 2 partials
tools.py 0.00% ⚠️ 88 Missing
runner.py 0.00% ⚠️ 87 Missing
beam.py 0.00% ⚠️ 84 Missing
loguru.py 11.58% ⚠️ 84 Missing
ray.py 0.00% ⚠️ 84 Missing
__init__.py 4.65% ⚠️ 82 Missing
asyncio.py 0.00% ⚠️ 80 Missing
invoke_agent.py 0.00% ⚠️ 79 Missing
session.py 15.56% ⚠️ 76 Missing
clickhouse_driver.py 17.58% ⚠️ 75 Missing
pure_eval.py 0.00% ⚠️ 73 Missing
rq.py 14.12% ⚠️ 73 Missing
worker.py 22.58% ⚠️ 72 Missing
ariadne.py 14.46% ⚠️ 71 Missing
gql.py 10.13% ⚠️ 71 Missing
sqlalchemy.py 10.26% ⚠️ 70 Missing
fastapi.py 15.85% ⚠️ 69 Missing
serializer.py 72.58% ⚠️ 51 Missing and 17 partials
monitoring.py 17.07% ⚠️ 68 Missing
graphene.py 12.82% ⚠️ 68 Missing
transactions.py 0.00% ⚠️ 67 Missing
_queue.py 26.67% ⚠️ 66 Missing
huey.py 17.72% ⚠️ 65 Missing
boto3.py 14.86% ⚠️ 63 Missing
utils.py 16.00% ⚠️ 63 Missing
httpx.py 16.44% ⚠️ 61 Missing
api.py 59.73% ⚠️ 60 Missing
chalice.py 16.18% ⚠️ 57 Missing
propagator.py 0.00% ⚠️ 57 Missing
streaming.py 0.00% ⚠️ 56 Missing
server.py 0.00% ⚠️ 56 Missing
spark_worker.py 0.00% ⚠️ 56 Missing
graph_nodes.py 0.00% ⚠️ 52 Missing
gnu_backtrace.py 0.00% ⚠️ 51 Missing
_async_common.py 0.00% ⚠️ 51 Missing
socket.py 0.00% ⚠️ 50 Missing
views.py 0.00% ⚠️ 50 Missing
caches.py 16.95% ⚠️ 49 Missing
_batcher.py 41.25% ⚠️ 47 Missing
invoke_agent.py 0.00% ⚠️ 46 Missing
traces.py 47.06% ⚠️ 45 Missing
_asgi_common.py 16.67% ⚠️ 45 Missing
signals_handlers.py 0.00% ⚠️ 44 Missing
utils.py 22.22% ⚠️ 42 Missing and 1 partials
client.py 0.00% ⚠️ 40 Missing
_sync_common.py 22.00% ⚠️ 39 Missing
executing.py 0.00% ⚠️ 38 Missing
client.py 0.00% ⚠️ 38 Missing
_span_batcher.py 35.71% ⚠️ 36 Missing
serverless.py 0.00% ⚠️ 36 Missing
threading.py 74.74% ⚠️ 24 Missing and 10 partials
server.py 0.00% ⚠️ 34 Missing
sys_exit.py 0.00% ⚠️ 32 Missing
scrubber.py 71.01% ⚠️ 20 Missing and 11 partials
trytond.py 0.00% ⚠️ 30 Missing
integration.py 0.00% ⚠️ 30 Missing
error_tracing.py 0.00% ⚠️ 29 Missing
tools.py 0.00% ⚠️ 27 Missing
ai_client.py 13.33% ⚠️ 26 Missing
execute_tool.py 0.00% ⚠️ 26 Missing
_openai_completions_api.py 19.35% ⚠️ 25 Missing
redis_cluster.py 26.47% ⚠️ 25 Missing
_werkzeug.py 11.11% ⚠️ 24 Missing
__init__.py 84.78% ⚠️ 14 Missing and 10 partials
typer.py 0.00% ⚠️ 24 Missing
tasks.py 0.00% ⚠️ 24 Missing
_compat.py 41.03% ⚠️ 23 Missing
monitor.py 67.21% ⚠️ 20 Missing and 3 partials
queries.py 25.81% ⚠️ 23 Missing
logger.py 43.59% ⚠️ 22 Missing
decorator.py 37.14% ⚠️ 22 Missing
_log_batcher.py 0.00% ⚠️ 21 Missing
attachments.py 27.59% ⚠️ 21 Missing
unraisablehook.py 0.00% ⚠️ 21 Missing
execute_tool.py 0.00% ⚠️ 20 Missing
unleash.py 0.00% ⚠️ 19 Missing
model_request.py 0.00% ⚠️ 18 Missing
utils.py 0.00% ⚠️ 16 Missing
atexit.py 56.25% ⚠️ 14 Missing and 1 partials
excepthook.py 56.25% ⚠️ 14 Missing and 1 partials
redis.py 25.00% ⚠️ 15 Missing
_init_implementation.py 45.83% ⚠️ 13 Missing
_lru_cache.py 63.33% ⚠️ 11 Missing and 2 partials
__init__.py 27.78% ⚠️ 13 Missing
_types.py 60.00% ⚠️ 12 Missing
types.py 0.00% ⚠️ 12 Missing
dedupe.py 80.00% ⚠️ 8 Missing and 3 partials
redis_py_cluster_legacy.py 26.67% ⚠️ 11 Missing
api.py 37.50% ⚠️ 10 Missing
utils.py 41.18% ⚠️ 10 Missing
handoff.py 0.00% ⚠️ 10 Missing
metrics.py 47.06% ⚠️ 9 Missing
__init__.py 72.41% ⚠️ 8 Missing and 1 partials
_metrics_batcher.py 65.00% ⚠️ 7 Missing
consts.py 0.00% ⚠️ 7 Missing
rb.py 30.00% ⚠️ 7 Missing
_openai_responses_api.py 33.33% ⚠️ 6 Missing
debug.py 91.30% ⚠️ 2 Missing and 2 partials
openfeature.py 90.00% ⚠️ 2 Missing and 2 partials
consts.py 0.00% ⚠️ 4 Missing
__init__.py 20.00% ⚠️ 4 Missing
__init__.py 0.00% ⚠️ 4 Missing
feature_flags.py 93.75% ⚠️ 2 Missing and 1 partials
launchdarkly.py 93.55% ⚠️ 2 Missing and 1 partials
__init__.py 0.00% ⚠️ 3 Missing
__init__.py 40.00% ⚠️ 3 Missing
__init__.py 0.00% ⚠️ 3 Missing
__init__.py 0.00% ⚠️ 3 Missing
__init__.py 0.00% ⚠️ 3 Missing
consts.py 99.43% ⚠️ 2 Missing
argv.py 100.00% ⚠️ 2 partials
modules.py 94.12% ⚠️ 1 Missing and 1 partials
statsig.py 91.30% ⚠️ 2 Missing
agent_workflow.py 71.43% ⚠️ 2 Missing
consts.py 0.00% ⚠️ 1 Missing
consts.py 0.00% ⚠️ 1 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    22.48%    22.66%    +0.18%
==========================================
  Files          189       189         —
  Lines        19830     19884       +54
  Branches      6428      6462       +34
==========================================
+ Hits          4457      4506       +49
- Misses       15373     15378        +5
- Partials       374       378        +4

Generated by Codecov Action

nonlocal ttft
count_tokens_manually = True
async for x in old_iterator:
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
Copy link
Contributor

Choose a reason for hiding this comment

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

Accessing x.model without defensive check could raise AttributeError and break streaming

The new code accesses x.model directly without checking if the attribute exists, and this call is placed outside the capture_internal_exceptions() block. If a streaming chunk doesn't have a model attribute (which could happen with certain API responses or edge cases), an AttributeError will propagate to the user's application and break their streaming logic. The rest of the code in this function uses defensive checks like hasattr(x, "choices") before accessing attributes.

Verification

Read the full function context at lines 658-686. Verified that the synchronous version at line 616 has the same pattern (also potentially problematic). Confirmed that capture_internal_exceptions() is used elsewhere in the file to protect against attribute access errors. Checked that set_data is a simple dictionary assignment that won't throw, so the issue is specifically with x.model access.

Suggested fix: Wrap the model attribute access inside the existing capture_internal_exceptions block or add a hasattr check

Suggested change
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
if hasattr(x, "model"):
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
Also found at 1 additional location
  • sentry_sdk/integrations/openai.py:616-616

Identified by Warden code-review · CWR-SCT

Copy link
Contributor

Choose a reason for hiding this comment

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

Fix attempt detected (commit f12cf5d)

The commit added unprotected x.model access outside the capture_internal_exceptions() block at both lines 616 and 662, directly introducing the exact issue reported: an AttributeError could propagate if a streaming chunk lacks the model attribute.

The original issue appears unresolved. Please review and try again.

Evaluated by Warden

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nonlocal ttft
count_tokens_manually = True
for x in old_iterator:
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
Copy link
Contributor

Choose a reason for hiding this comment

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

Unprotected attribute access on streaming chunk may cause runtime error

The x.model access on line 616 is placed outside the capture_internal_exceptions() block, unlike other model accesses in this file (e.g., lines 761, 810 which are inside the block, or line 472 which uses hasattr guard). If a streaming chunk lacks the model attribute or it's malformed, an AttributeError will propagate to the user's application instead of being silently logged by Sentry's internal error handler.

Verification

Read openai.py lines 590-670 and 740-815. Verified pattern: line 472 uses if hasattr(response, 'model') guard, lines 761 and 810 place model access inside capture_internal_exceptions(). The new code at line 616 has neither protection and could raise AttributeError to user code.

Suggested fix: Move the set_data call inside the existing capture_internal_exceptions() block

Suggested change
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
if hasattr(x, "model"):
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)

Identified by Warden code-review · 26J-GUA

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link

@cursor cursor bot left a comment

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.

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

nonlocal ttft
count_tokens_manually = True
for x in old_iterator:
span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
Copy link

Choose a reason for hiding this comment

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

Ungarded streamed model access can raise

Medium Severity

x.model is read directly in the streaming iterators before capture_internal_exceptions(). If a streamed chunk does not expose model (or uses a dict-like/event variant), this raises and interrupts iteration, making instrumentation break the caller’s stream instead of failing safely.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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