Skip to content

fix: preserve nested attachments in dataclass fields - #912

Open
Gareth.Y (linjiaye929) wants to merge 2 commits into
braintrustdata:mainfrom
linjiaye929:codex/dataclass-nested-attachments
Open

Gareth.Y (linjiaye929) wants to merge 2 commits into
braintrustdata:mainfrom
linjiaye929:codex/dataclass-nested-attachments

Conversation

@linjiaye929

Copy link
Copy Markdown

Dataclass input/output with attachments inside a list, dict, or tuple loses those attachments during serialization. The field takes a string-encoding path, so the log contains <braintrust.logger.Attachment object at ...> and attachment extraction finds nothing to upload. Direct attachment fields already work.

Route dataclasses through the existing deep-copy walker. This preserves nested attachment identity, shares cycle/depth tracking across dataclasses and containers, and keeps the existing fallback for unreadable fields.

Tests cover direct and nested fields, dataclasses inside lists, shared references, cycles, depth limits, ordinary JSON fields, and unreadable fields. Logger input/output tests verify that both Attachment and ExternalAttachment reach flush-time extraction without making upload requests or mutating the original payload.

Validation on Python 3.12.14:

  • Same focused tests against unchanged main: 8 failed, 6 passed. With the fix: 14 passed.
  • Exact repository nox -s test_core session: 990 passed, 73 skipped, 12 xfailed. This is the core suite without optional provider SDKs.
  • Full test_bt_json.py and test_logger.py with orjson 3.13.0 and Pydantic 2.14.0: 235 passed, 3 skipped.
  • All repository pre-commit hooks passed; targeted pylint --errors-only and git diff --check passed.
  • Existing serialization benchmarks ran before and after as a local smoke check; no general performance claim.

Checks used isolated environments. make fixup could not run because mise is unavailable, so its pre-commit run --all-files command was run directly. Other Python versions, provider integration suites, and real attachment uploads were not tested locally.

AI assistance was used to implement and test this change.

@linjiaye929
Gareth.Y (linjiaye929) marked this pull request as ready for review October 12, 2026 10:32
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T10:37:26.339747Z 2e27140 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e27140463

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

visited_discard(obj_id)

try:
if dataclasses.is_dataclass(v) and not isinstance(v, type):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve dataclass-based BaseAttachment instances

When a user implements the public BaseAttachment ABC as a dataclass, this branch matches before _to_bt_safe() can apply its BaseAttachment check, so bt_safe_deep_copy() converts the attachment into a plain field dictionary. _extract_attachments() only uploads values that remain BaseAttachment instances, so logging this otherwise-valid custom attachment silently skips its upload and reference replacement; the parent implementation preserved it by passing through _to_bt_safe(). Check the special attachment types before flattening dataclasses.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed and fixed in 30c6cbe. Dataclass BaseAttachment and ReadonlyAttachment subclasses now retain their existing special handling before generic dataclass traversal. Added 14 regression cases covering direct and nested serialization plus local logger extraction; all failed on the reviewed head and pass with the fix. The original dataclass regressions also remain green (28 focused tests total), and test_core passes 1004 tests. Logger coverage verifies extraction/scheduling and reference replacement without real uploads. AI-assisted follow-up.

This branch has not been deployed

No deployments
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.

1 participant