Split `preserve_file_data` into `allow_uploaded_files` (inbound security) and an AG-UI representation opt-in
Merged
DO
4mo ago+303+303+012345678901234567890123456789-63-63-01234567890123456789
Summary
preserve_file_data on the UI adapters had quietly grown to cover two unrelated concerns under one name, with a security framing that only ever applied to one of them:
- Inbound (security): whether to honor client-submitted [
UploadedFile][pydantic_ai.messages.UploadedFile] references. These are dangerous from an untrusted client because the model provider resolves thefile_idusing the server's own credentials (like a non-HTTPFileUrl), so they're dropped by default. - AG-UI representation: whether to round-trip
FilePart/UploadedFilethrough reservedpydantic_ai_*sidecar activity messages, since the AG-UI protocol has no native slot for them.
It also implied an outbound "files stay on the server" trust model that never actually applied ā file content the agent produces is always sent to the client (that's the point of a multimodal UI).
This PR untangles the two:
UIAdapter.allow_uploaded_files(new) is the inbound security gate, replacing the basepreserve_file_data. DefaultFalse; clientUploadedFiles are dropped bysanitize_messagesunless it's set.preserve_file_datais now a deprecated alias onfrom_request/dispatch_requestā it maps ontoallow_uploaded_filesand emitsPydanticAIDeprecationWarning.AGUIAdapter.preserve_file_datakeeps its original representation meaning (theFilePart/UploadedFilesidecar round-trip). It no longer gates inbound security: anUploadedFilereconstructed from a round-tripped activity message is still subject toallow_uploaded_filesvia the sharedsanitize_messagesstep, so representation and security compose cleanly and independently.
Behavior / breaking notes
- AG-UI behavior change: honoring client-submitted uploaded files now requires
allow_uploaded_files=Truein addition topreserve_file_data=True(previously the single flag did both). Emitting the outbound sidecars is unchanged (preserve_file_data=True). - Deprecation: passing
preserve_file_data=toVercelAIAdapterā via the constructor,from_request, ordispatch_requestā still works and emitsPydanticAIDeprecationWarning, mapping ontoallow_uploaded_files. The alias lives on the concreteVercelAIAdapter(not the baseUIAdapter), becausepreserve_file_datais a real field onAGUIAdapterand can't also be a deprecated member on the base without breaking typing. A customUIAdaptersubclass that used the old base field should switch toallow_uploaded_files.
Notes
- This work was split out while reconciling #5255; it lands on
mainfirst so #5255 (and #6169) can rebase onto the clarified flags. - Tests:
tests/test_ui.py,tests/test_ag_ui.py,tests/test_vercel_ai.pyall green (387 passed); pyright clean on the UI package + test files.
š¤ Generated with Claude Code