Skip to content

fix(pydantic-ai): Use first-class hooks when available#5947

Open
alexander-alderman-webb wants to merge 34 commits intomasterfrom
webb/pydantic-ai/move-instruction-fetching
Open

fix(pydantic-ai): Use first-class hooks when available#5947
alexander-alderman-webb wants to merge 34 commits intomasterfrom
webb/pydantic-ai/move-instruction-fetching

Conversation

@alexander-alderman-webb
Copy link
Copy Markdown
Contributor

@alexander-alderman-webb alexander-alderman-webb commented Apr 7, 2026

Description

Resolve tox failure by using the pydantic-ai hooks when available. See the failing run in #5945

See changes to instruction setting in https://github.com/pydantic/pydantic-ai/pull/4123/changes#diff-71730070455237accebd709ba7fe41ce60edbd238af02e7d86a208cf8f10ab12. Per-run system instructions are now added inside ModelRequestNode.run and ModelRequestNode.stream, and are therefore not available before the functions run.

Issues

Reminders

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 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).


New Features ✨

  • (ai) Redact base64 data URLs in image_url content blocks by ericapisani in #5953
  • (integrations) Instrument pyreqwest tracing by servusdei2018 in #5682

Bug Fixes 🐛

Anthropic

  • Capture exceptions for stream() calls by alexander-alderman-webb in #5950
  • Stop setting transaction status when child span fails by alexander-alderman-webb in #5717
  • Only finish relevant spans in .create() patches by alexander-alderman-webb in #5716

Other

  • (pydantic-ai) Use first-class hooks when available by alexander-alderman-webb in #5947
  • (wsgi) Respect HTTP_X_FORWARDED_PROTO in request.url construction by sl0thentr0py in #5963

Internal Changes 🔧

  • (anthropic) Separate sync and async .create() patches by alexander-alderman-webb in #5715
  • (openai) Split token counting by API for easier deprecation by ericapisani in #5930
  • (opentelemetry) Ignore mypy error by alexander-alderman-webb in #5927
  • Fix license metadata in setup.py by sl0thentr0py in #5934
  • Update validate-pr workflow by stephanie-anderson in #5931

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Codecov Results 📊

13 passed | Total: 13 | Pass Rate: 100% | Execution Time: 11.23s

All tests are passing successfully.

❌ Patch coverage is 4.23%. Project has 14925 uncovered lines.

Files with missing lines (2)
File Patch % Lines
agent_run.py 0.00% ⚠️ 100 Missing
__init__.py 9.21% ⚠️ 69 Missing

Generated by Codecov Action

@alexander-alderman-webb alexander-alderman-webb marked this pull request as ready for review April 7, 2026 10:46
@alexander-alderman-webb alexander-alderman-webb requested a review from a team as a code owner April 7, 2026 10:46
Copy link
Copy Markdown

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

Autofix Details

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

  • ✅ Fixed: Streaming path sets input messages before model execution
    • Moved _set_input_messages in the streaming wrapper to execute after the original stream context finishes so instruction mutations done during execution are captured.

Create PR

Or push these changes by commenting:

@cursor push 6153a56a6a
Preview (6153a56a6a)
diff --git a/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py b/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py
--- a/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py
+++ b/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py
@@ -91,13 +91,13 @@
 
             # Create chat span for streaming request
             with ai_client_span(None, model, model_settings) as span:
-                if messages:
-                    _set_input_messages(span, messages)
-
                 # Call the original stream method
                 async with original_stream_method(self, ctx) as stream:
                     yield stream
 
+                if messages:
+                    _set_input_messages(span, messages)
+
                 # After streaming completes, update span with response data
                 # The ModelRequestNode stores the final response in _result
                 model_response = None

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

@alexander-alderman-webb alexander-alderman-webb marked this pull request as draft April 7, 2026 11:23
@alexander-alderman-webb alexander-alderman-webb changed the base branch from webb/pydantic-ai/remove-test to master April 8, 2026 13:42
@alexander-alderman-webb alexander-alderman-webb marked this pull request as ready for review April 9, 2026 13:33
@alexander-alderman-webb alexander-alderman-webb changed the title fix(pydantic-ai): Set system instructions after instructions are set on the request object fix(pydantic-ai): Use first-class hooks when available Apr 9, 2026
Copy link
Copy Markdown

@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 2 potential issues.

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 ffcb5f3. Configure here.

if PydanticAIIntegration.are_request_hooks_available:
metadata = kwargs.get("metadata")
if not metadata:
kwargs["metadata"] = {"_sentry_span": None}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Metadata check inconsistency overwrites user-provided empty dicts

Low Severity

The run wrappers use if not metadata: which treats any falsy value (including {}) as missing, while patched_init in register_hooks correctly uses if metadata is None:. A user who explicitly passes metadata={} to run() or run_stream() will have their metadata silently replaced with {"_sentry_span": None}. Using if metadata is None: would be consistent with the patched_init check and correctly distinguish "not provided" from "provided as empty dict".

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ffcb5f3. Configure here.


return original_init(self, *args, **kwargs)

Agent.__init__ = patched_init
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pre-existing agents silently lose chat span instrumentation

Medium Severity

register_hooks patches Agent.__init__ to inject hooks into capabilities, but agents created before sentry_init() never go through patched_init and thus lack hooks. Those agents will still get invoke_agent and execute_tool spans (patched at class level), but will silently produce no gen_ai.chat spans. This is a regression from the monkey-patching fallback path, which patches ModelRequestNode/Model at the class level and works for all agent instances regardless of creation time.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ffcb5f3. Configure here.

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