Repository navigation
Commit fff5bd2
fix(pydantic_ai): allowlist kwargs pass-through, drop eager message reshape (#610)
## Summary
Aligns the pydantic_ai integration with
`.agents/skills/sdk-integrations/SKILL.md`, following the same pattern
as #606 (openai), #608 (openrouter), and #604/#603/#594
(anthropic/livekit/litellm/mistral). Audit turned up two issues:
- **Denylist metadata pass-through (real leak vector).**
`_build_agent_input_and_metadata` and
`_build_direct_model_input_and_metadata` did `for key, value in
kwargs.items(): if key not in [...]: input_data[key] = value` — every
non-`deps` / non-`{model,messages}` kwarg landed in span input. That
flowed `usage` (a mutable counter), `event_stream_handler` (a callable),
`infer_name`, `instrument`, and anything pydantic_ai adds in future SDK
versions straight into telemetry. `deps` (user's app-level container —
commonly holds API keys / DB connections) was the sole excluded key on
the agent path; on the direct-model path there was no exclusion at all.
Replaced with per-surface allowlist tuples (`_AGENT_INPUT_ALLOWLIST`,
`_DIRECT_MODEL_INPUT_ALLOWLIST`) covering the known kwargs across
`Agent.run` / `run_sync` / `run_stream` / `run_stream_events` /
`to_cli_sync` and `direct.model_request*`. Explicitly excludes `deps`,
`usage`, `event_stream_handler`, `infer_name`, `instrument`.
- **Eager message reshaping dropping fields.** `_shape_message` /
`_shape_content_part` / `_shape_model_response` reshaped every message
into shallow dicts via a hard-coded field allowlist (`_MESSAGE_FIELDS`,
`_PART_FIELDS`, `_RESPONSE_FIELDS`) even when no binary content needed
materialization. That dropped real pydantic_ai v2 fields —
`instructions`, `run_id`, `conversation_id`, `metadata`, `state`,
`provider_details`, `outcome`, ... — and burned CPU rebuilding dicts
that `bt_safe_deep_copy` (which already handles dataclasses + Pydantic
models) would produce anyway at log time.
Added `_has_binary_leaf` walker; the four shape helpers now
short-circuit to the raw object when no binary is present. Reshape (and
its field allowlist) only runs when a binary attachment actually needs
materialization.
Also:
- Extracted `_shape_toolset(ts)` from a nested 20-line block inside
`_build_agent_input_and_metadata`.
- Trimmed docstrings/comments that just restated the helper name. Kept
the ones explaining non-obvious *why* (portal-thread context
propagation, wrapper-vs-leaf token ownership, 1.78.0 ToolManager alias,
`_system_prompts` private-API access).
- Hoisted `import inspect` from inside `_shape_type` to module top.
## Test plan
- [x] **No new mocks/fakes.** All provider-behavior coverage stays on
the existing `@pytest.mark.vcr` cassette-backed tests.
- [x] **No cassette re-recording.** HTTP behavior unchanged; only
in-process input/shape logic was touched.
- [x] `nox -s "test_pydantic_ai_integration(latest)"` — 68/68 pass
(`pydantic-ai==2.13.0`)
- [x] `nox -s "test_pydantic_ai_integration(1.10.0)"` — 68/68 pass
(min-supported matrix version)
- [x] `nox -s "test_pydantic_ai_wrap_openai(latest)"` — green
- [x] `nox -s "test_pydantic_ai_logfire(latest)"` — 1/1 pass
Two new tests pin the security invariant:
- `test_agent_input_kwargs_are_allowlisted` — asserts `deps` / `usage` /
`event_stream_handler` / `infer_name` never land in span input;
allowlisted kwargs (`instructions`, `usage_limits`, `retries`,
`conversation_id`, `metadata`) do.
- `test_direct_model_request_kwargs_are_allowlisted` — asserts
`instrument` never leaks; `model_settings` / `model_request_parameters`
do.
One existing test replaced:
`test_v2_message_and_response_fields_are_shaped` →
`test_shape_message_and_response_pass_through_when_no_binary` — the old
test was locking in the reshape that was losing v2 fields.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- sfk:created-approved-by -->
Created by abhijeet
<!-- sfk:slack-thread -->
[Slack
thread](https://starfolkai.slack.com/archives/C0AQDETAVT3/p1784659060950489?thread_ts=1784659060.950489&cid=C0AQDETAVT3)
---------
Co-authored-by: Starfolk <noreply@starfolk.ai>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent a1a1b11 commit fff5bd2
3 files changed
Lines changed: 151 additions & 270 deletions
File tree
- py/src/braintrust/integrations/pydantic_ai
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
87 | | - | |
88 | | - | |
89 | 87 | | |
90 | 88 | | |
91 | 89 | | |
| |||
152 | 150 | | |
153 | 151 | | |
154 | 152 | | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
| 153 | + | |
159 | 154 | | |
160 | 155 | | |
161 | 156 | | |
| |||
191 | 186 | | |
192 | 187 | | |
193 | 188 | | |
194 | | - | |
195 | | - | |
196 | | - | |
197 | | - | |
198 | | - | |
| 189 | + | |
199 | 190 | | |
200 | 191 | | |
201 | 192 | | |
| |||
Lines changed: 34 additions & 26 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
709 | 709 | | |
710 | 710 | | |
711 | 711 | | |
712 | | - | |
| 712 | + | |
| 713 | + | |
713 | 714 | | |
714 | 715 | | |
715 | 716 | | |
716 | 717 | | |
717 | 718 | | |
718 | 719 | | |
719 | | - | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
720 | 727 | | |
721 | 728 | | |
722 | | - | |
723 | 729 | | |
724 | 730 | | |
725 | | - | |
726 | 731 | | |
727 | | - | |
728 | 732 | | |
729 | 733 | | |
730 | | - | |
731 | 734 | | |
732 | 735 | | |
733 | 736 | | |
| |||
739 | 742 | | |
740 | 743 | | |
741 | 744 | | |
742 | | - | |
743 | 745 | | |
744 | 746 | | |
745 | | - | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
746 | 750 | | |
747 | 751 | | |
748 | 752 | | |
| |||
1103 | 1107 | | |
1104 | 1108 | | |
1105 | 1109 | | |
1106 | | - | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
1107 | 1114 | | |
1108 | 1115 | | |
1109 | 1116 | | |
1110 | 1117 | | |
1111 | 1118 | | |
1112 | | - | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
| 1123 | + | |
| 1124 | + | |
| 1125 | + | |
1113 | 1126 | | |
1114 | 1127 | | |
1115 | 1128 | | |
1116 | 1129 | | |
1117 | | - | |
1118 | 1130 | | |
1119 | 1131 | | |
1120 | 1132 | | |
1121 | | - | |
1122 | 1133 | | |
1123 | 1134 | | |
1124 | 1135 | | |
| |||
1135 | 1146 | | |
1136 | 1147 | | |
1137 | 1148 | | |
| 1149 | + | |
| 1150 | + | |
| 1151 | + | |
| 1152 | + | |
| 1153 | + | |
1138 | 1154 | | |
1139 | 1155 | | |
1140 | 1156 | | |
| |||
2370 | 2386 | | |
2371 | 2387 | | |
2372 | 2388 | | |
2373 | | - | |
| 2389 | + | |
| 2390 | + | |
| 2391 | + | |
| 2392 | + | |
2374 | 2393 | | |
2375 | 2394 | | |
2376 | 2395 | | |
2377 | 2396 | | |
2378 | 2397 | | |
2379 | | - | |
| 2398 | + | |
2380 | 2399 | | |
2381 | 2400 | | |
2382 | 2401 | | |
2383 | 2402 | | |
2384 | 2403 | | |
2385 | 2404 | | |
2386 | | - | |
2387 | | - | |
2388 | | - | |
2389 | | - | |
2390 | 2405 | | |
2391 | 2406 | | |
2392 | | - | |
2393 | | - | |
2394 | | - | |
2395 | | - | |
2396 | | - | |
2397 | | - | |
2398 | | - | |
2399 | | - | |
| 2407 | + | |
2400 | 2408 | | |
2401 | 2409 | | |
2402 | 2410 | | |
| |||
0 commit comments