Skip to content

fix(cua): use native file interfaces for uploads#8069

Merged
zouyonghe merged 1 commit into
AstrBotDevs:masterfrom
zouyonghe:fix/cua-files-upload-8054
May 7, 2026
Merged

fix(cua): use native file interfaces for uploads#8069
zouyonghe merged 1 commit into
AstrBotDevs:masterfrom
zouyonghe:fix/cua-files-upload-8054

Conversation

@zouyonghe
Copy link
Copy Markdown
Member

@zouyonghe zouyonghe commented May 7, 2026

Summary

  • route CUA uploads through the SDK-native files.upload and files.write_bytes interfaces before falling back to shell base64 writes
  • resolve file methods across both sandbox.files and legacy sandbox.filesystem so mixed SDK environments keep working without regressing to shell behavior
  • add regression tests for native upload success, native upload failure payloads, native filesystem reads/writes, and mixed files/filesystem compatibility

Problem

Issue #8054 failed after the CUA sandbox booted, during skill bundle sync. AstrBot packed local skills into skills.zip, then CuaBooter.upload_file() fell back to embedding the whole base64 payload into one shell command. On larger bundles that command exceeded the remote command length limit and the sandbox returned [Errno 7] Argument list too long.

Root Cause

The CUA adapter only checked sandbox.upload_file and otherwise used the shell fallback. The current CUA SDK exposes file transfer and file IO through sandbox.files, while some older environments may still expose sandbox.filesystem. Because AstrBot was not using those interfaces, it missed the native upload path and overused shell-based file writes.

Fix

This change teaches the CUA adapter to discover file methods across both sandbox.files and sandbox.filesystem on a per-method basis.

For uploads, CuaBooter.upload_file() now tries:

  1. sandbox.upload_file
  2. files.upload
  3. files.write_bytes
  4. existing shell base64 fallback

For filesystem operations, CuaFileSystemComponent now resolves each operation against both modern and legacy interfaces so mixed SDK environments still use the native method that actually exists.

The native upload path also now preserves structured failure payloads instead of always reporting success.

Validation

  • uv run pytest tests/test_computer_skill_sync.py tests/unit/test_cua_computer_use.py -q
  • uv run ruff check astrbot/core/computer/booters/cua.py tests/unit/test_cua_computer_use.py

Summary by Sourcery

Prefer native CUA sandbox file interfaces for uploads and filesystem operations before falling back to shell-based behavior.

New Features:

  • Add support for resolving file operations across both sandbox.files and legacy sandbox.filesystem interfaces on a per-method basis.
  • Enable CUA upload_file to use native files.upload and files.write_bytes interfaces when available.

Bug Fixes:

  • Preserve and propagate structured failure payloads from native file upload operations instead of always reporting success.

Tests:

  • Add unit tests covering native files interface usage, legacy filesystem fallback behavior, and native upload success and failure paths for CUA.

@zouyonghe zouyonghe marked this pull request as ready for review May 7, 2026 16:56
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend labels May 7, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In _normalize_native_upload_result, consider guarding against non-mapping payloads from _maybe_model_dump (e.g., booleans or strings) so that accessing keys like "file_path" and "success" does not raise when a native upload returns a primitive instead of a dict/model.
  • The new _resolve_files_components/_resolve_files_method helpers are now called on every filesystem operation; if these code paths are hot, you may want to cache the resolved methods per operation (e.g., in CuaFileSystemComponent and CuaBooter) to avoid repeatedly walking the components tuple.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_normalize_native_upload_result`, consider guarding against non-mapping payloads from `_maybe_model_dump` (e.g., booleans or strings) so that accessing keys like `"file_path"` and `"success"` does not raise when a native upload returns a primitive instead of a dict/model.
- The new `_resolve_files_components`/`_resolve_files_method` helpers are now called on every filesystem operation; if these code paths are hot, you may want to cache the resolved methods per operation (e.g., in `CuaFileSystemComponent` and `CuaBooter`) to avoid repeatedly walking the components tuple.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces helper functions to standardize file system operations and upload result normalization within the CuaShellComponent, along with corresponding unit tests. The changes effectively decouple file system interactions and improve robustness. The reviewer provided actionable feedback suggesting an improvement to the file_path mapping logic in the normalization helper and recommended refactoring legacy upload calls to utilize this new helper for improved consistency.

Comment on lines +265 to +266
if "file_path" not in payload and "path" not in payload:
payload["file_path"] = file_name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The current logic fails to ensure the file_path key is present if the payload contains a path key instead. According to the ComputerBooter.upload_file contract, the returned dictionary must contain a file_path key. If path is present in the native response, it should be mapped to file_path to satisfy the interface requirements. Additionally, as this handles file attachments, ensure this logic is accompanied by unit tests.

Suggested change
if "file_path" not in payload and "path" not in payload:
payload["file_path"] = file_name
if "file_path" not in payload:
payload["file_path"] = payload.get("path") or file_name
References
  1. New functionality, such as handling attachments, should be accompanied by corresponding unit tests.

Comment on lines 841 to 843
return _maybe_model_dump(
await sandbox.upload_file(str(local_path), file_name)
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For consistency and to strictly adhere to the upload_file contract (which requires success and file_path keys), the legacy sandbox.upload_file result should also be processed by _normalize_native_upload_result. This refactors the logic into a shared helper function to avoid code duplication and ensures that new attachment handling functionality is properly normalized and tested.

            result = await sandbox.upload_file(str(local_path), file_name)
            return _normalize_native_upload_result(result, file_name)
References
  1. When implementing similar functionality for different cases (e.g., direct vs. quoted attachments), refactor the logic into a shared helper function to avoid code duplication.
  2. New functionality, such as handling attachments, should be accompanied by corresponding unit tests.

@zouyonghe zouyonghe merged commit 5745ce5 into AstrBotDevs:master May 7, 2026
21 checks passed
murphys7017 pushed a commit to murphys7017/AstrBot that referenced this pull request May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant