Skip to content

file transfer (python): parse images/files, wire directive sink to Rust parity - #344

Merged
brentrager merged 1 commit into
mainfrom
ft-py
Aug 11, 2026
Merged

file transfer (python): parse images/files, wire directive sink to Rust parity#344
brentrager merged 1 commit into
mainfrom
ft-py

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

The Python smooth-operator-server was behind the Rust reference on the file-transfer contract merged in #342 (send_message.files[] + the send_file directive convention). Its send_message path was text-only: images[] were ignored, files[] had nowhere to land, and there was no tool→directive channel onto eventual_response.

Solution

Parity with the Rust server (handler.rs/runner.rs/tool_provider.rs/protocol.rs):

  • images[] → attached to the model's user message as OpenAI image_url content parts (multimodal turns). Done in-tree in turn_runner against the pinned published core (run_stream(message) drops it straight onto the user message) — no core release needed. Fail-soft, and guarded: falls back to text-only when a knowledge base is wired, because the pinned core reuses the same message for its retrieval query (a content list would crash tokenization). Clean fix lands when the core grows a dedicated user-images seam (feature-detect then, as with _CORE_SUPPORTS_*).
  • files[] → parsed fail-soft onto a new per-turn TurnContext (the Python analog of Rust's ToolProviderContext), surfaced for a host tool to land into the workspace. Never sent to the model.
  • directive sinkTurnContext carries a last-write-wins directive a host tool writes (e.g. the send_file convention). Drained after the turn onto TurnResult.directive and emitted as eventual_response.directive — omitted when none (back-compat).
  • Regenerated the Python SDK types (_generated.py) from the updated spec (files[] + directive docs; also picks up the already-merged skill field).

All fail-soft and back-compatible: a text-only turn with no attachments is byte-identical to before.

Verification

  • New tests/test_file_transfer.py (14 tests): image→content-parts mapping, fail-soft parsing (_build_user_content / _parse_attachments), images-attach integration, files-stay-off-model, tool→directive→TurnResult, directive last-write-wins, eventual_response directive present/absent, dispatcher directive forwarding + attachment parsing.
  • Gates green per python.yml for both projects: ruff check, ruff format --check, pytest — server 246 passed / 1 skipped, core 52 passed / 1 skipped.
  • Changeset added (minor, @smooai/smooth-operator-server).

🤖 Generated with Claude Code

…st parity

Bring the Python smooth-operator server to parity with the Rust reference on the
file-transfer contract (spec PR #342):

- images[]: attached to the model's user message as OpenAI image_url content
  parts (multimodal turns), in-tree in turn_runner against the pinned published
  core — no core release needed. Guarded fail-soft: falls back to text-only when
  a knowledge base is wired (the pinned core reuses the message for retrieval).
- files[]: parsed fail-soft onto a new per-turn TurnContext (the Python analog of
  Rust's ToolProviderContext), surfaced for a host tool to land in the workspace;
  never sent to the model.
- directive sink: TurnContext carries a last-write-wins directive a host tool
  writes (e.g. the send_file convention); drained after the turn onto
  TurnResult.directive and emitted on eventual_response.directive (omitted when
  none — back-compat).
- Regenerated the SDK types from the updated spec (files[] + directive docs).
- Tests: images-attach, files-on-context, tool->directive->eventual_response,
  fail-soft parsing, dispatcher forwarding + attachment parsing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9f82d96

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/smooth-operator-server Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager merged commit 20e8c1f into main Aug 11, 2026
2 checks passed
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