Skip to content

fix(transport): support Anthropic list-form system prompts (types + CLI)#900

Open
zion-off wants to merge 2 commits into
anthropics:mainfrom
zion-off:899
Open

fix(transport): support Anthropic list-form system prompts (types + CLI)#900
zion-off wants to merge 2 commits into
anthropics:mainfrom
zion-off:899

Conversation

@zion-off
Copy link
Copy Markdown

@zion-off zion-off commented May 1, 2026

  • Summary: Fix a crash when callers pass Anthropic-style list-form system prompts. The subprocess CLI transport previously assumed non-string system_prompt values were mappings and called .get() on them, raising AttributeError for lists. This change makes the SDK accept list-form system prompts in the public types and forwards them to the bundled Claude CLI by writing a temporary JSON file and passing it via the existing --system-prompt-file flag. The temporary file is cleaned up on error or transport close.

  • Changes:

    • Types: Add SystemPromptBlocks and allow list[dict[str, Any]] for ClaudeAgentOptions.system_prompt to reflect Anthropic Messages API shape. See types.py.
    • Transport: SubprocessCLITransport now detects list-form system prompts, writes them to a temp JSON file, passes --system-prompt-file <path> to the CLI, and removes the temp file on close/error. See subprocess_cli.py.
    • Tests: Add regression tests for both the types and transport behavior. See test_types.py and test_transport.py.
  • Motivation: Upstream Anthropic docs allow system to be either a string or a list of content blocks (to support per-block metadata like cache_control). Previously the SDK crashed early on list inputs; this patch preserves the list form and forwards it unchanged to the CLI, enabling correct caching semantics and avoiding forcing callers to flatten blocks into a string.

Fixes #899.

Copy link
Copy Markdown

@seeincodes seeincodes left a comment

Choose a reason for hiding this comment

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

Drive-by review (not a maintainer; flagging two issues that look load-bearing).

The shape of the change is good — public type alias, materialize/cleanup helpers, cleanup wired into close() and the existing connect() error branches. Tests cover the round-trip and the close-time cleanup.

Two issues worth addressing before merge:

  1. Tempfile leaks on cancellation during connect(). _build_command() at subprocess_cli.py:423 creates the tempfile, then the connect() try block awaits anyio.open_process() at line 474. If the parent task is cancelled during that await (or anywhere else inside the try), the cancellation is BaseException-derived (anyio's CancelledError / trio.Cancelled), so it bypasses both except FileNotFoundError and except Exception at lines 502 / 513 — and the tempfile leaks. Same shape for KeyboardInterrupt. Likely fix: structure as try/finally that runs cleanup whenever self._ready never gets set to True, rather than relying on except branches.

  2. Cleanup uses suppress(FileNotFoundError) only. On Windows, if the CLI subprocess still has the file open when path.unlink() runs, you get PermissionError, not FileNotFoundError. In practice the CLI reads --system-prompt-file at startup and closes it, so this is unlikely to bite, but worth either widening the suppression to (FileNotFoundError, PermissionError) or noting the assumption.

Smaller things:

  • The memoization in _materialize_system_prompt_blocks (skips creating a new file if _generated_system_prompt_file is not None) is fragile if the same transport instance ever gets _build_command()-then-cleanup-then-_build_command() with different blocks. Cleanup nulls the field so this path is currently unreachable, but a fresh-tempfile-per-call model would be simpler to reason about.
  • Test gap: no coverage for the cleanup-on-error path. Mocking anyio.open_process to raise before _ready=True and asserting the temp file is gone would catch (1) above as a regression test.

Nothing blocking on the happy path.

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.

subprocess CLI rejects list-form system_prompt (Anthropic API supports it)

2 participants