Skip to content

feat: add tool_code_execution() built-in tool (OpenAI, Anthropic, Google)#332

Draft
cpsievert wants to merge 9 commits into
mainfrom
worktree-code-execution-tools
Draft

feat: add tool_code_execution() built-in tool (OpenAI, Anthropic, Google)#332
cpsievert wants to merge 9 commits into
mainfrom
worktree-code-execution-tools

Conversation

@cpsievert

Copy link
Copy Markdown
Collaborator

Summary

Adds tool_code_execution(), a provider-agnostic built-in tool that lets
ChatOpenAI()/ChatAnthropic()/ChatGoogle() write and run code in each
provider's own server-side sandbox -- useful for math, data analysis, or
letting the model check its own logic, without chatlas needing to expose a
local, unsandboxed execution path. Follows the same pattern as the existing
tool_web_search()/tool_web_fetch() built-ins.

v1 scope: text output only. Only stdout/stderr are surfaced as structured
content. Files the code produces (plots, CSVs, etc.) are NOT downloaded or
decoded -- the raw provider data is still available via each content's
extra field, but there's no first-class support for those files yet.

Before merging -- open item to decide on

  • Anthropic tool version is pinned to the oldest working release
    (code_execution_20250522). Newer versions (20250825/20260120/20260521)
    route execution through a bash_code_execution sub-tool chatlas doesn't
    parse yet, so this PR deliberately stays on the oldest version rather
    than adding that parsing now. Confirm we're OK shipping v1 this way, and
    that we won't forget to revisit if Anthropic deprecates this version.

What changed

  • New ContentToolRequestCodeExecution/ContentToolResponseCodeExecution
    content types, and a ToolCodeExecution/tool_code_execution() built-in
    tool, following the existing web-search/fetch precedent.
  • Provider wiring for OpenAI (code_interpreter), Anthropic (code_execution
    beta tool), and Google (code_execution) -- each translating to that
    provider's native tool shape and parsing its native response shape back
    into the shared content types.
  • OpenAI and Anthropic reuse the same sandbox/container across turns in a
    conversation automatically (so e.g. a variable defined in one turn is
    available in the next, for OpenAI); Google starts fresh each turn, since
    Gemini has no cross-turn persistence mechanism.
  • One correction worth flagging: live testing showed Claude's code execution
    does not actually persist Python interpreter state across turns, contrary
    to Anthropic's own SDK docs -- only OpenAI does. Docs/docstrings describe
    this accurately rather than over-promising.
  • Fixed a related bug found along the way: Anthropic's container id was only
    ever captured from non-streaming responses -- the streaming accumulator
    silently dropped it, so container reuse never actually worked via the
    default chat.chat() path. Now fixed and regression-tested.

Test plan

  • Unit tests (mock provider responses) for request-building and
    response-parsing across all three providers
  • VCR-recorded live-conversation tests for OpenAI, Anthropic, and Google
  • Container-reuse tests for OpenAI and Anthropic, including a regression
    test for the streaming container-capture fix
  • Full suite: 615 passed, 12 skipped
  • make check-types / make check-format clean

…ent)

Widen last_code_execution_container_id's parameter to Sequence[Turn]
instead of list[Turn] so callers can pass list[AssistantTurn] without
tripping pyright's list invariance (matches precedent in _chat.py and
_turn.py). Also documents the type: ignore in
ToolCodeExecution._openai_definition per project convention.
Adds live-conversation tests for the code execution built-in tool across
all three providers, plus a "Code execution" section in the tools guide.

Live testing against the real Anthropic API revealed two inaccuracies in
the code execution wiring merged earlier: the documented beta header
(code-execution-2026-05-21) doesn't exist -- the correct value is
code-execution-2025-05-22, paired with tool version code_execution_20250522
(the newer 20260521 version routes calls through a bash/text-editor bundle
chatlas doesn't parse). Also, contrary to the tool's own docstring, Claude's
code execution does not persist Python interpreter state across separate
turns (only OpenAI's does), so no persistence test/claim is made for
Claude, matching the existing treatment of Google.
Recorded against live OpenAI, Anthropic, and Google APIs; scanned clean
for secrets by scripts/check_vcr_secrets.py.
The streaming response accumulator only copied stop_reason/stop_sequence/
usage from message_delta events, silently dropping the container field
Anthropic sends there (never on message_start). Since chat.chat() streams
by default, this meant code execution's cross-turn container reuse never
actually worked outside of stream=False -- confirmed live and closed by
the final whole-branch review's one Important finding.
@cpsievert
cpsievert force-pushed the worktree-code-execution-tools branch from de57072 to 1bb43f8 Compare July 21, 2026 23:34
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