Pass pydantic_core through the workflow sandbox by default - #1834
Open
DABH wants to merge 2 commits into
Open
Conversation
The OpenAI Agents replay tests intermittently failed on slow CI runners with "[TMPRL1101] Potential deadlock detected" and a stack showing the workflow thread inside the sandbox importer executing pydantic_core/core_schema.py. `pydantic` is in the default passthrough list but `pydantic_core` was not, and openai's `_compat.field_get_default` imports it lazily from the plugin's payload converter. So the first `resolve_activity` activation of every sandboxed workflow re-executed the ~4.6k-line pydantic_core package inside the sandbox, inside the 2s deadlock budget, serialized with every other workflow thread doing the same through the import lock and the GIL. Pass pydantic_core through alongside pydantic, for the same reason pydantic is passed through: Pydantic-based libraries import it lazily. It is pydantic's version-locked compiled core with no workflow state, and its extension objects were already shared across sandboxes. Three contrib plugins (strands, google_genai, deepagents) already pass it through explicitly. Unloaded, the first activation drops from 6-57ms to 1-2ms and no module is copied into the sandbox during activations anymore. 128 concurrent first activations went from 0.7s max latency (serialized import) to ~0. Under emulated CPU starvation (background QoS plus 72 CPU burners) the in-sandbox import took 0.5-0.9s per workflow; passthrough takes ~0. The replay tests now also assert that no module is imported into the sandbox after initial workflow load, so a reintroduced in-activation import fails deterministically instead of as a timing-dependent deadlock.
There was a problem hiding this comment.
🟢 Approval recommended
The focused implementation and regression test correctly address the reported sandbox import overhead.
Pull request overview
Adds pydantic_core to default sandbox passthrough modules to prevent costly lazy imports during workflow activation.
Changes:
- Adds default
pydantic_corepassthrough. - Adds replay regression coverage for dynamic imports.
- Documents the fix.
File summaries
| File | Description |
|---|---|
temporalio/worker/workflow_sandbox/_restrictions.py |
Adds the default passthrough. |
tests/contrib/openai_agents/test_openai_replay.py |
Verifies replay causes no activation-time imports. |
CHANGELOG.md |
Documents the behavior change. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
DABH
marked this pull request as ready for review
September 10, 2026 08:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was changed
pydantic_corejoinspydanticin the sandbox's default passthrough list. Regression assertion in the OpenAI Agents replay test and CHANGELOG entry.Why
Libraries built on Pydantic import
pydantic_corelazily (openai._compatdoes on the first activity result), so each sandbox re-imported about 4.6k lines of it during its first activation, inside the deadlock-detector budget. On slow CI runners (the source of the motivating observation behind this PR), that trippedPotential deadlock detectedin the replay tests. The compiled core has no time, random or IO behavior, and its extension objects were already shared across sandboxes.Testing
First activation with an activity result: 6-57ms before, 1-2ms after; under emulated starvation 925ms max before, 0.1ms after. Replay tests 35/35 under load on 3.10 and 3.14. The exact 2s trip did not reproduce locally; the CI traces are the evidence for it.