From 2b1b3ea45799f025ea558386f313c45ac8f968eb Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 13:37:39 -0500 Subject: [PATCH 1/9] feat: add ContentToolRequestCodeExecution and ContentToolResponseCodeExecution --- chatlas/_content.py | 76 ++++++++++++++++++++++++++++ chatlas/types/__init__.py | 4 ++ tests/test_content_code_execution.py | 69 +++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 tests/test_content_code_execution.py diff --git a/chatlas/_content.py b/chatlas/_content.py index 2243343e..6deba7cd 100644 --- a/chatlas/_content.py +++ b/chatlas/_content.py @@ -146,6 +146,8 @@ def from_tool(cls, tool: "Tool | ToolBuiltIn") -> "ToolInfo": "web_search_results", "web_fetch_request", "web_fetch_results", + "code_execution_request", + "code_execution_result", ] """ A discriminated union of all content types. @@ -839,6 +841,74 @@ def __str__(self): return f"[web fetch result]: {self.url}" +class ContentToolRequestCodeExecution(Content): + """ + A code execution request from the model. + + This content type represents the model's request to run code in a + sandboxed environment. It's automatically generated when a built-in code + execution tool is used. + + Parameters + ---------- + code + The code the model wants to run. + language + The programming language of `code`, if the provider reports it. + extra + The raw provider-specific response data. + """ + + code: str + language: Optional[str] = None + extra: Optional[dict[str, Any]] = None + + content_type: ContentTypeEnum = "code_execution_request" + + def __str__(self): + return f"[code execution request]:\n```\n{self.code}\n```" + + +class ContentToolResponseCodeExecution(Content): + """ + Code execution results from the model. + + This content type represents the result of running code in a sandboxed + environment. It's automatically generated when a built-in code execution + tool returns results. + + Only text output (stdout/stderr or each provider's equivalent) is + surfaced here. Files the code produced (e.g. plots, CSVs) aren't + downloaded or decoded -- their raw provider references are still + available via `extra`. + + Parameters + ---------- + output + The text output of the code (e.g. stdout). + error + The error output of the code (e.g. stderr), if any. + container_id + The id of the sandbox/container the code ran in, if the provider + exposes one. Used by `chatlas.Chat` to reuse the same sandbox across + turns for providers that support it (OpenAI, Anthropic). + extra + The raw provider-specific response data. + """ + + output: Optional[str] = None + error: Optional[str] = None + container_id: Optional[str] = None + extra: Optional[dict[str, Any]] = None + + content_type: ContentTypeEnum = "code_execution_result" + + def __str__(self): + if self.error: + return f"[code execution error]:\n```\n{self.error}\n```" + return f"[code execution result]:\n```\n{self.output or ''}\n```" + + ContentUnion = Union[ ContentText, ContentImageRemote, @@ -852,6 +922,8 @@ def __str__(self): ContentToolResponseSearch, ContentToolRequestFetch, ContentToolResponseFetch, + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, ] @@ -917,6 +989,10 @@ def create_content(data: dict[str, Any]) -> ContentUnion: return ContentToolRequestFetch.model_validate(data) elif ct == "web_fetch_results": return ContentToolResponseFetch.model_validate(data) + elif ct == "code_execution_request": + return ContentToolRequestCodeExecution.model_validate(data) + elif ct == "code_execution_result": + return ContentToolResponseCodeExecution.model_validate(data) else: raise ValueError(f"Unknown content type: {ct}") diff --git a/chatlas/types/__init__.py b/chatlas/types/__init__.py index a6c5e580..408a0724 100644 --- a/chatlas/types/__init__.py +++ b/chatlas/types/__init__.py @@ -14,8 +14,10 @@ ContentThinking, ContentThinkingDelta, ContentToolRequest, + ContentToolRequestCodeExecution, ContentToolRequestFetch, ContentToolRequestSearch, + ContentToolResponseCodeExecution, ContentToolResponseFetch, ContentToolResponseSearch, ContentToolResult, @@ -46,6 +48,8 @@ "ContentToolRequestSearch", "ContentToolResponseSearch", "FinishReason", + "ContentToolRequestCodeExecution", + "ContentToolResponseCodeExecution", "StructuredChatResult", "ChatResponse", "ChatResponseAsync", diff --git a/tests/test_content_code_execution.py b/tests/test_content_code_execution.py new file mode 100644 index 00000000..c5662bd2 --- /dev/null +++ b/tests/test_content_code_execution.py @@ -0,0 +1,69 @@ +from chatlas._content import ( + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, + ContentUnion, + create_content, +) + + +def test_content_tool_request_code_execution_defaults(): + content = ContentToolRequestCodeExecution(code="print(1 + 1)") + assert content.content_type == "code_execution_request" + assert content.code == "print(1 + 1)" + assert content.language is None + assert content.extra is None + + +def test_content_tool_request_code_execution_str(): + content = ContentToolRequestCodeExecution(code="print(1 + 1)") + assert "print(1 + 1)" in str(content) + + +def test_content_tool_response_code_execution_defaults(): + content = ContentToolResponseCodeExecution(output="2") + assert content.content_type == "code_execution_result" + assert content.output == "2" + assert content.error is None + assert content.container_id is None + assert content.extra is None + + +def test_content_tool_response_code_execution_str_with_output(): + content = ContentToolResponseCodeExecution(output="2") + assert "2" in str(content) + + +def test_content_tool_response_code_execution_str_with_error(): + content = ContentToolResponseCodeExecution(error="NameError: x is not defined") + assert "NameError" in str(content) + + +def test_create_content_round_trips_request(): + content = ContentToolRequestCodeExecution( + code="print(1 + 1)", language="PYTHON", extra={"id": "abc"} + ) + data = content.model_dump() + restored = create_content(data) + assert isinstance(restored, ContentToolRequestCodeExecution) + assert restored.code == "print(1 + 1)" + assert restored.language == "PYTHON" + assert restored.extra == {"id": "abc"} + + +def test_create_content_round_trips_response(): + content = ContentToolResponseCodeExecution( + output="2", container_id="cntr_123", extra={"id": "abc"} + ) + data = content.model_dump() + restored = create_content(data) + assert isinstance(restored, ContentToolResponseCodeExecution) + assert restored.output == "2" + assert restored.container_id == "cntr_123" + + +def test_content_union_includes_code_execution_types(): + import typing + + args = typing.get_args(ContentUnion) + assert ContentToolRequestCodeExecution in args + assert ContentToolResponseCodeExecution in args From f39d747921650666bfe22cf1383f30c99f76011d Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 13:43:20 -0500 Subject: [PATCH 2/9] feat: add ToolCodeExecution and tool_code_execution() --- chatlas/__init__.py | 3 +- chatlas/_tools_builtin.py | 200 +++++++++++++++++++++++++++++++++++- tests/test_tools_builtin.py | 138 +++++++++++++++++++++++-- 3 files changed, 325 insertions(+), 16 deletions(-) diff --git a/chatlas/__init__.py b/chatlas/__init__.py index 60fe8658..f9fc6d54 100644 --- a/chatlas/__init__.py +++ b/chatlas/__init__.py @@ -38,7 +38,7 @@ from ._stream_controller import StreamController from ._tokens import token_usage from ._tools import Tool, ToolBuiltIn, ToolRejectError -from ._tools_builtin import tool_web_fetch, tool_web_search +from ._tools_builtin import tool_code_execution, tool_web_fetch, tool_web_search from ._turn import AssistantTurn, SystemTurn, Turn, UserTurn try: @@ -93,6 +93,7 @@ "Tool", "ToolBuiltIn", "ToolRejectError", + "tool_code_execution", "tool_web_fetch", "tool_web_search", "Turn", diff --git a/chatlas/_tools_builtin.py b/chatlas/_tools_builtin.py index 356a35bb..7f0f404f 100644 --- a/chatlas/_tools_builtin.py +++ b/chatlas/_tools_builtin.py @@ -1,9 +1,9 @@ """ -Built-in provider tools for web search and fetch. +Built-in provider tools for web search, web fetch, and code execution. These classes provide a provider-agnostic way to configure built-in tools -like web search and URL fetching. Each provider translates these configurations -into their specific API format. +like web search, URL fetching, and code execution. Each provider translates +these configurations into their specific API format. """ from __future__ import annotations @@ -11,12 +11,16 @@ import warnings from typing import TYPE_CHECKING, Literal, Optional, overload -from ._content import ToolAnnotations +from ._content import ContentToolResponseCodeExecution, ToolAnnotations from ._tools import ToolBuiltIn +from ._turn import AssistantTurn, Turn if TYPE_CHECKING: from anthropic.types import WebSearchTool20250305Param - from anthropic.types.beta import BetaWebFetchTool20250910Param + from anthropic.types.beta import ( + BetaCodeExecutionTool20260521Param, + BetaWebFetchTool20250910Param, + ) from anthropic.types.beta.beta_citations_config_param import ( BetaCitationsConfigParam, ) @@ -29,7 +33,9 @@ PhishBlockThreshold, UrlContext, ) + from google.genai.types import ToolCodeExecution as GoogleToolCodeExecution from openai.types.responses import WebSearchToolParam + from openai.types.responses.tool_param import CodeInterpreter from ._typing_extensions import TypedDict @@ -49,8 +55,10 @@ class UserLocation(TypedDict, total=False): __all__ = ( "tool_web_search", "tool_web_fetch", + "tool_code_execution", "ToolWebSearch", "ToolWebFetch", + "ToolCodeExecution", ) @@ -643,3 +651,185 @@ async def main(): blocked_domains=blocked_domains, max_uses=max_uses, ) + + +class ToolCodeExecution(ToolBuiltIn): + """ + A provider-agnostic code execution tool configuration. + + This class stores configuration for server-side code execution + functionality. Each provider translates this configuration into their + specific API format. + """ + + def __init__(self): + _annotations: ToolAnnotations = { + "title": "Code execution", + "readOnlyHint": False, + "openWorldHint": False, + } + super().__init__( + name="code_execution", + definition={}, + description="Execute code in a sandboxed environment.", + annotations=_annotations, + ) + + @overload + def get_definition( + self, + provider_name: Literal["openai"], + *, + container_id: Optional[str] = None, + ) -> "CodeInterpreter": ... + + @overload + def get_definition( + self, + provider_name: Literal["anthropic"], + ) -> "BetaCodeExecutionTool20260521Param": ... + + @overload + def get_definition( + self, + provider_name: Literal["google"], + ) -> "GoogleToolCodeExecution": ... + + def get_definition( + self, + provider_name: Literal["openai", "anthropic", "google"], + *, + # OpenAI-specific + container_id: Optional[str] = None, + ) -> ( + "CodeInterpreter | BetaCodeExecutionTool20260521Param | GoogleToolCodeExecution" + ): + """ + Get the provider-specific tool definition. + + Parameters + ---------- + provider_name + The name of the provider ('openai', 'anthropic', or 'google'). + container_id + OpenAI only. Reuse an existing sandbox container instead of + starting a fresh one. `chatlas.Chat` sets this automatically + from prior turns; you shouldn't need to pass it yourself. + + Returns + ------- + : + The provider-specific tool definition. + """ + if provider_name == "openai": + return self._openai_definition(container_id=container_id) + elif provider_name == "anthropic": + return self._anthropic_definition() + elif provider_name == "google": + return self._google_definition() + else: + raise ValueError( + f"Code execution is not supported for provider '{provider_name}'. " + "Supported providers: openai, anthropic, google." + ) + + @staticmethod + def _openai_definition(*, container_id: Optional[str] = None) -> "CodeInterpreter": + """Generate OpenAI code interpreter tool definition.""" + # https://platform.openai.com/docs/guides/tools-code-interpreter + container = container_id or {"type": "auto"} + return {"type": "code_interpreter", "container": container} # type: ignore + + @staticmethod + def _anthropic_definition() -> "BetaCodeExecutionTool20260521Param": + """Generate Anthropic/Claude code execution tool definition.""" + # https://docs.claude.com/en/docs/agents-and-tools/tool-use/code-execution-tool + return { + "name": "code_execution", + "type": "code_execution_20260521", + } + + @staticmethod + def _google_definition() -> "GoogleToolCodeExecution": + """Generate Google/Gemini code execution tool definition.""" + # https://ai.google.dev/gemini-api/docs/code-execution + from google.genai.types import ToolCodeExecution as GoogleToolCodeExecution + + return GoogleToolCodeExecution() + + +def tool_code_execution() -> ToolCodeExecution: + """ + Create a code execution tool for use with chat models. + + This function creates a provider-agnostic code execution tool that can be + registered with any supported chat provider. The tool allows the model to + write and run code in a sandboxed environment and see the result -- e.g. + for math, data analysis, or verifying its own logic. + + Supported providers: OpenAI, Claude (Anthropic), Google (Gemini) + + Prerequisites + ------------- + - **OpenAI**: Code execution is available by default with the Responses API. + - **Claude**: The code execution tool requires the beta header + `anthropic-beta: code-execution-2026-05-21`. Pass this via the `kwargs` + parameter's `default_headers` option (see examples below). + - **Google**: Code execution is available by default with Gemini. + + Returns + ------- + ToolCodeExecution + A code execution tool that can be registered with `chat.register_tool()`. + + Examples + -------- + ```python + from chatlas import ChatOpenAI, tool_code_execution + + chat = ChatOpenAI() + chat.register_tool(tool_code_execution()) + chat.chat("What's the 20th Fibonacci number?") + ``` + + ```python + from chatlas import ChatAnthropic, tool_code_execution + + chat = ChatAnthropic( + kwargs={"default_headers": {"anthropic-beta": "code-execution-2026-05-21"}} + ) + chat.register_tool(tool_code_execution()) + chat.chat("What's the 20th Fibonacci number?") + ``` + + Note + ---- + Only text output (stdout/stderr, or each provider's equivalent) is + surfaced as structured content. Files the code produces (e.g. plots, + CSVs) aren't downloaded or decoded -- their raw provider references are + still available via each content's `extra` attribute. + + OpenAI and Claude reuse the same sandbox across turns in a conversation + automatically, so a variable defined in one turn is still available in + the next. Google starts a fresh sandbox on every turn. + """ + return ToolCodeExecution() + + +def last_code_execution_container_id(turns: list["Turn"]) -> Optional[str]: + """ + Find the most recent code execution container/sandbox id in turn history. + + Providers that support session reuse (OpenAI, Anthropic) key off this to + reuse the same sandbox across turns instead of starting a fresh one. + """ + for turn in reversed(turns): + if not isinstance(turn, AssistantTurn): + continue + for content in reversed(turn.contents): + if ( + isinstance(content, ContentToolResponseCodeExecution) + and content.container_id + ): + return content.container_id + return None diff --git a/tests/test_tools_builtin.py b/tests/test_tools_builtin.py index ec20d314..b261e963 100644 --- a/tests/test_tools_builtin.py +++ b/tests/test_tools_builtin.py @@ -1,17 +1,22 @@ """Tests for built-in web search and fetch tools.""" import pytest - from chatlas import ( ChatAnthropic, ChatGoogle, ChatOpenAI, + tool_code_execution, tool_web_fetch, tool_web_search, ) from chatlas._content import ToolAnnotations from chatlas._tools import ToolBuiltIn -from chatlas._tools_builtin import ToolWebFetch, ToolWebSearch +from chatlas._tools_builtin import ( + ToolCodeExecution, + ToolWebFetch, + ToolWebSearch, + last_code_execution_container_id, +) class TestToolBuiltInMetadata: @@ -84,9 +89,7 @@ def test_cannot_use_both_domain_filters(self): with pytest.raises( ValueError, match="Cannot specify both allowed_domains and blocked_domains" ): - tool_web_search( - allowed_domains=["good.com"], blocked_domains=["bad.com"] - ) + tool_web_search(allowed_domains=["good.com"], blocked_domains=["bad.com"]) def test_with_user_location(self): """Test web search tool with user location.""" @@ -127,9 +130,7 @@ def test_cannot_use_both_domain_filters(self): with pytest.raises( ValueError, match="Cannot specify both allowed_domains and blocked_domains" ): - tool_web_fetch( - allowed_domains=["good.com"], blocked_domains=["bad.com"] - ) + tool_web_fetch(allowed_domains=["good.com"], blocked_domains=["bad.com"]) class TestToolWebSearchProviderDefinitions: @@ -205,7 +206,9 @@ def test_unsupported_provider(self): def test_openai_warns_on_unsupported_params(self): """Test that OpenAI warns about unsupported parameters.""" tool = tool_web_search(blocked_domains=["spam.com"], max_uses=5) - with pytest.warns(UserWarning, match="blocked_domains is not supported by OpenAI"): + with pytest.warns( + UserWarning, match="blocked_domains is not supported by OpenAI" + ): tool.get_definition("openai") def test_google_warns_on_unsupported_params(self): @@ -287,7 +290,9 @@ def test_google_warns_on_unsupported_params(self): assert any("allowed_domains" in m for m in messages) assert any("max_uses" in m for m in messages) - with pytest.warns(UserWarning, match="blocked_domains is not supported by Google"): + with pytest.warns( + UserWarning, match="blocked_domains is not supported by Google" + ): tool2.get_definition("google") def test_anthropic_with_extra_params(self): @@ -372,3 +377,116 @@ def add(x: int, y: int) -> int: assert len(tools) == 2 tool_names = {t.name for t in tools} assert tool_names == {"web_search", "add"} + + +class TestToolCodeExecutionConfiguration: + """Test ToolCodeExecution configuration and provider definitions.""" + + def test_has_description_and_annotations(self): + tool = tool_code_execution() + assert tool.description == "Execute code in a sandboxed environment." + assert tool.annotations is not None + assert tool.annotations["title"] == "Code execution" + assert tool.annotations["readOnlyHint"] is False + assert tool.annotations["openWorldHint"] is False + + def test_basic_configuration(self): + tool = tool_code_execution() + assert isinstance(tool, ToolCodeExecution) + assert tool.name == "code_execution" + + def test_openai_definition_defaults_to_auto_container(self): + tool = tool_code_execution() + definition = tool.get_definition("openai") + assert definition["type"] == "code_interpreter" + assert definition["container"] == {"type": "auto"} + + def test_openai_definition_reuses_container_id(self): + tool = tool_code_execution() + definition = tool.get_definition("openai", container_id="cntr_abc123") + assert definition["container"] == "cntr_abc123" + + def test_anthropic_definition(self): + tool = tool_code_execution() + definition = tool.get_definition("anthropic") + assert definition["name"] == "code_execution" + assert definition["type"] == "code_execution_20260521" + + def test_google_definition(self): + from google.genai.types import ToolCodeExecution as GoogleToolCodeExecution + + tool = tool_code_execution() + definition = tool.get_definition("google") + assert isinstance(definition, GoogleToolCodeExecution) + + def test_unsupported_provider(self): + tool = tool_code_execution() + with pytest.raises(ValueError, match="Code execution is not supported"): + tool.get_definition("unsupported_provider") + + +class TestLastCodeExecutionContainerId: + """Test the turn-history scan that powers cross-turn sandbox reuse.""" + + def test_returns_none_with_no_turns(self): + assert last_code_execution_container_id([]) is None + + def test_returns_none_when_no_code_execution_response(self): + from chatlas._content import ContentText + from chatlas._turn import AssistantTurn + + turns = [AssistantTurn([ContentText(text="hi")])] + assert last_code_execution_container_id(turns) is None + + def test_finds_container_id_from_last_assistant_turn(self): + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._turn import AssistantTurn, UserTurn + + turns = [ + UserTurn("define x = 1"), + AssistantTurn( + [ContentToolResponseCodeExecution(output="", container_id="cntr_1")] + ), + UserTurn("now print x"), + AssistantTurn( + [ContentToolResponseCodeExecution(output="1", container_id="cntr_2")] + ), + ] + assert last_code_execution_container_id(turns) == "cntr_2" + + def test_ignores_response_without_container_id(self): + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._turn import AssistantTurn + + turns = [ + AssistantTurn( + [ContentToolResponseCodeExecution(output="1", container_id="cntr_1")] + ), + AssistantTurn([ContentToolResponseCodeExecution(output="2")]), + ] + assert last_code_execution_container_id(turns) == "cntr_1" + + +class TestChatRegistrationCodeExecution: + """Test registering the code execution tool with chat instances.""" + + def test_register_code_execution_openai(self): + chat = ChatOpenAI() + chat.register_tool(tool_code_execution()) + tools = chat.get_tools() + assert len(tools) == 1 + assert tools[0].name == "code_execution" + + def test_register_code_execution_anthropic(self): + chat = ChatAnthropic() + chat.register_tool(tool_code_execution()) + tools = chat.get_tools() + assert len(tools) == 1 + assert tools[0].name == "code_execution" + + def test_register_code_execution_google(self): + chat = ChatGoogle() + chat.register_tool(tool_code_execution()) + tools = chat.get_tools() + assert len(tools) == 1 + assert tools[0].name == "code_execution" From 9f12a64c646aef0193e954e1a0463518ee5bf7b4 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 16:25:19 -0500 Subject: [PATCH 3/9] fix: address task 2 review feedback (Sequence[Turn], type:ignore comment) 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. --- chatlas/_tools_builtin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chatlas/_tools_builtin.py b/chatlas/_tools_builtin.py index 7f0f404f..e1ae8180 100644 --- a/chatlas/_tools_builtin.py +++ b/chatlas/_tools_builtin.py @@ -9,7 +9,7 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Literal, Optional, overload +from typing import TYPE_CHECKING, Literal, Optional, Sequence, overload from ._content import ContentToolResponseCodeExecution, ToolAnnotations from ._tools import ToolBuiltIn @@ -738,6 +738,7 @@ def _openai_definition(*, container_id: Optional[str] = None) -> "CodeInterprete """Generate OpenAI code interpreter tool definition.""" # https://platform.openai.com/docs/guides/tools-code-interpreter container = container_id or {"type": "auto"} + # dict[str, str] doesn't structurally satisfy CodeInterpreter's container union return {"type": "code_interpreter", "container": container} # type: ignore @staticmethod @@ -816,7 +817,7 @@ def tool_code_execution() -> ToolCodeExecution: return ToolCodeExecution() -def last_code_execution_container_id(turns: list["Turn"]) -> Optional[str]: +def last_code_execution_container_id(turns: Sequence["Turn"]) -> Optional[str]: """ Find the most recent code execution container/sandbox id in turn history. From 67bae9cabf35da7db5b7a0abacc22b7495156d60 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 16:31:51 -0500 Subject: [PATCH 4/9] feat: support code execution tool in OpenAI provider --- chatlas/_provider_openai.py | 48 ++++++++++++++- tests/test_provider_openai.py | 110 +++++++++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 4 deletions(-) diff --git a/chatlas/_provider_openai.py b/chatlas/_provider_openai.py index 28c402cb..605a606c 100644 --- a/chatlas/_provider_openai.py +++ b/chatlas/_provider_openai.py @@ -20,7 +20,9 @@ ContentThinking, ContentThinkingDelta, ContentToolRequest, + ContentToolRequestCodeExecution, ContentToolRequestSearch, + ContentToolResponseCodeExecution, ContentToolResult, ) from ._logging import log_model_default @@ -28,7 +30,12 @@ from ._provider_openai_completions import load_tool_request_args from ._provider_openai_generic import BatchResult, OpenAIAbstractProvider from ._tools import Tool, ToolBuiltIn, basemodel_to_param_schema -from ._tools_builtin import ToolWebFetch, ToolWebSearch +from ._tools_builtin import ( + ToolCodeExecution, + ToolWebFetch, + ToolWebSearch, + last_code_execution_container_id, +) from ._turn import AssistantTurn, FinishReason, Turn if TYPE_CHECKING: @@ -272,6 +279,11 @@ def _chat_perform_args( "Consider using the MCP Fetch server instead via chat.register_mcp_tools_stdio_async(). " "See help(tool_web_fetch) for details." ) + elif isinstance(tool, ToolCodeExecution): + container_id = last_code_execution_container_id(turns) + tool_params.append( + tool.get_definition("openai", container_id=container_id) + ) elif isinstance(tool, ToolBuiltIn): tool_params.append(cast("ToolParam", tool.definition)) else: @@ -465,6 +477,23 @@ def _response_as_turn(completion: Response, has_data_model: bool) -> AssistantTu ) ) + elif output.type == "code_interpreter_call": + # https://platform.openai.com/docs/guides/tools-code-interpreter#understanding-code-interpreter-output + contents.append( + ContentToolRequestCodeExecution( + code=output.code or "", + extra=output.model_dump(), + ) + ) + logs = [o.logs for o in (output.outputs or []) if o.type == "logs"] + contents.append( + ContentToolResponseCodeExecution( + output="\n".join(logs) or None, + container_id=output.container_id, + extra=output.model_dump(), + ) + ) + else: raise ValueError(f"Unknown output type: {output.type}") @@ -487,7 +516,11 @@ def _response_as_turn(completion: Response, has_data_model: bool) -> AssistantTu def _turns_as_inputs(self, turns: list[Turn]) -> "list[ResponseInputItemParam]": res: "list[ResponseInputItemParam]" = [] for turn in turns: - res.extend([as_input_param(x, turn.role) for x in turn.contents]) + res.extend( + x + for x in (as_input_param(c, turn.role) for c in turn.contents) + if x is not None + ) return res def translate_model_params(self, params: StandardModelParams) -> "SubmitInputArgs": @@ -525,7 +558,9 @@ def _batch_endpoint(): return "/v1/responses" -def as_input_param(content: Content, role: Role) -> "ResponseInputItemParam": +def as_input_param( + content: Content, role: Role +) -> "Optional[ResponseInputItemParam]": if isinstance(content, ContentText): if role == "assistant": # OpenAI's type for this value (ResponseOutputMessageParam) currently has a bunch @@ -598,6 +633,13 @@ def as_input_param(content: Content, role: Role) -> "ResponseInputItemParam": } elif isinstance(content, ContentToolRequestSearch): return cast("ResponseInputItemParam", content.extra) + elif isinstance(content, ContentToolRequestCodeExecution): + return cast("ResponseInputItemParam", content.extra) + elif isinstance(content, ContentToolResponseCodeExecution): + # OpenAI bundles code + result into a single code_interpreter_call + # item, already captured by the paired + # ContentToolRequestCodeExecution's extra above. + return None else: raise ValueError(f"Unsupported content type: {type(content)}") diff --git a/tests/test_provider_openai.py b/tests/test_provider_openai.py index 544986ba..53a5f082 100644 --- a/tests/test_provider_openai.py +++ b/tests/test_provider_openai.py @@ -2,7 +2,7 @@ import httpx import pytest -from chatlas import ChatOpenAI, tool_web_search +from chatlas import ChatOpenAI, tool_code_execution, tool_web_search from chatlas._provider_openai import ( normalize_finish_reason as openai_normalize_finish_reason, ) @@ -336,3 +336,111 @@ def test_openai_custom_base_url_warning(): with warnings.catch_warnings(): warnings.simplefilter("error") check_base_url("https://api.openai.com/v1") + + +def test_openai_code_execution_call_parses_request_and_response(): + """A code_interpreter_call output splits into request + response content.""" + from chatlas._content import ( + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, + ) + from chatlas._provider_openai import OpenAIProvider + + chat = ChatOpenAI() + provider = chat.provider + assert isinstance(provider, OpenAIProvider) + + def make_response(outputs: list[dict]): + from openai.types.responses import Response + + return Response.model_validate( + { + "id": "resp_1", + "created_at": 0, + "model": "gpt-4.1", + "object": "response", + "output": [ + { + "id": "ci_1", + "type": "code_interpreter_call", + "status": "completed", + "code": "print(1 + 1)", + "container_id": "cntr_abc123", + "outputs": outputs, + } + ], + "parallel_tool_calls": True, + "tool_choice": "auto", + "tools": [], + } + ) + + resp = make_response([{"type": "logs", "logs": "2"}]) + turn = provider._response_as_turn(resp, has_data_model=False) + + assert len(turn.contents) == 2 + request = turn.contents[0] + assert isinstance(request, ContentToolRequestCodeExecution) + assert request.code == "print(1 + 1)" + + response = turn.contents[1] + assert isinstance(response, ContentToolResponseCodeExecution) + assert response.output == "2" + assert response.container_id == "cntr_abc123" + + +def test_openai_code_execution_response_does_not_duplicate_on_round_trip(): + """The response half shouldn't be resubmitted -- it's bundled into the request's extra.""" + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._provider_openai import as_input_param + + content = ContentToolResponseCodeExecution( + output="2", extra={"type": "code_interpreter_call"} + ) + assert as_input_param(content, "assistant") is None + + +def test_openai_code_execution_tool_uses_auto_container_by_default(): + from chatlas._provider_openai import OpenAIProvider + + chat = ChatOpenAI() + chat.register_tool(tool_code_execution()) + provider = chat.provider + assert isinstance(provider, OpenAIProvider) + + kwargs = provider._chat_perform_args( + stream=False, + turns=[], + tools=chat._tools, # type: ignore[reportPrivateUsage] + data_model=None, + kwargs=None, + ) + tools = kwargs["tools"] + assert tools[0]["type"] == "code_interpreter" + assert tools[0]["container"] == {"type": "auto"} + + +def test_openai_code_execution_tool_reuses_container_from_turn_history(): + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._provider_openai import OpenAIProvider + from chatlas._turn import AssistantTurn + + chat = ChatOpenAI() + chat.register_tool(tool_code_execution()) + provider = chat.provider + assert isinstance(provider, OpenAIProvider) + + turns = [ + AssistantTurn( + [ContentToolResponseCodeExecution(output="1", container_id="cntr_xyz")] + ) + ] + kwargs = provider._chat_perform_args( + stream=False, + turns=turns, + tools=chat._tools, # type: ignore[reportPrivateUsage] + data_model=None, + kwargs=None, + ) + tools = kwargs["tools"] + assert tools[0]["container"] == "cntr_xyz" From 99fb5c3bc2e886b1ef551396c3f21901dee4fdeb Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 16:39:42 -0500 Subject: [PATCH 5/9] feat: support code execution tool in Anthropic provider --- chatlas/_provider_anthropic.py | 66 ++++++++++++++- tests/test_provider_anthropic.py | 134 +++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 4 deletions(-) diff --git a/chatlas/_provider_anthropic.py b/chatlas/_provider_anthropic.py index 1892c230..c055c68b 100644 --- a/chatlas/_provider_anthropic.py +++ b/chatlas/_provider_anthropic.py @@ -28,8 +28,10 @@ ContentThinking, ContentThinkingDelta, ContentToolRequest, + ContentToolRequestCodeExecution, ContentToolRequestFetch, ContentToolRequestSearch, + ContentToolResponseCodeExecution, ContentToolResponseFetch, ContentToolResponseSearch, ContentToolResult, @@ -44,7 +46,12 @@ ) from ._tokens import get_price_info from ._tools import Tool, ToolBuiltIn, basemodel_to_param_schema -from ._tools_builtin import ToolWebFetch, ToolWebSearch +from ._tools_builtin import ( + ToolCodeExecution, + ToolWebFetch, + ToolWebSearch, + last_code_execution_container_id, +) from ._turn import AssistantTurn, FinishReason, SystemTurn, Turn, UserTurn, user_turn from ._utils import split_http_client_kwargs @@ -492,6 +499,13 @@ def _chat_perform_args( **(kwargs or {}), } + if "container" not in kwargs_full and any( + isinstance(tool, ToolCodeExecution) for tool in tools.values() + ): + container_id = last_code_execution_container_id(turns) + if container_id is not None: + kwargs_full["container"] = container_id + if data_model is not None and use_native: kwargs_full["output_config"] = output_config # type: ignore[reportPossiblyUnbound] elif data_model is not None: @@ -716,9 +730,13 @@ def _as_message_params(self, turns: Sequence[Turn]) -> list["MessageParam"]: # https://docs.claude.com/en/docs/build-with-claude/prompt-caching#how-automatic-prefix-checking-works is_last_turn = i == len(turns) - 1 if self._cache_control() and is_last_turn and len(content) > 0: - # Note: ThinkingBlockParam (i.e., type: "thinking") doesn't support cache_control - if content[-1].get("type") != "thinking": - content[-1]["cache_control"] = self._cache_control() # type: ignore + last_block = content[-1] + # Note: ThinkingBlockParam (i.e., type: "thinking") doesn't support + # cache_control. `last_block` can also be None for round-tripped + # server tool content missing `extra` (e.g. manually constructed + # in tests), in which case there's nothing to attach cache_control to. + if last_block is not None and last_block.get("type") != "thinking": + last_block["cache_control"] = self._cache_control() # type: ignore role = "user" if isinstance(turn, UserTurn) else "assistant" messages.append({"role": role, "content": content}) @@ -788,6 +806,8 @@ def _as_content_block(content: Content) -> "ContentBlockParam": ContentToolResponseSearch, ContentToolRequestFetch, ContentToolResponseFetch, + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, ), ): # extra contains the full original content block param @@ -803,6 +823,9 @@ def _anthropic_tool_schema(tool: "Tool | ToolBuiltIn") -> "ToolUnionParam": # N.B. seems the return type here (BetaWebFetchTool20250910Param) is # not a member of ToolUnionParam since it's still in beta? return tool.get_definition("anthropic") # type: ignore + if isinstance(tool, ToolCodeExecution): + # N.B. same beta-type situation as ToolWebFetch above. + return tool.get_definition("anthropic") # type: ignore if isinstance(tool, ToolBuiltIn): return tool.definition # type: ignore @@ -907,6 +930,13 @@ def _as_turn(self, completion: Message, has_data_model=False) -> AssistantTurn: extra=extra, ) ) + elif content.name == "code_execution": + contents.append( + ContentToolRequestCodeExecution( + code=str(input_data.get("code", "")), + extra=extra, + ) + ) else: raise ValueError(f"Unknown server tool: {content.name}") elif content.type == "web_search_tool_result": @@ -954,6 +984,34 @@ def _as_turn(self, completion: Message, has_data_model=False) -> AssistantTurn: extra=extra, ) ) + elif content.type == "code_execution_tool_result": + # https://docs.claude.com/en/docs/agents-and-tools/tool-use/code-execution-tool#result + result = content.content + if result.type == "code_execution_tool_result_error": + output = None + error = result.error_code + elif result.type == "code_execution_result": + output = result.stdout or None + error = result.stderr or None + else: + # encrypted_code_execution_result: stdout isn't available in plaintext + output = None + error = result.stderr or None + extra = { + "type": content.type, + "tool_use_id": content.tool_use_id, + "content": result.model_dump(exclude_none=True), + } + contents.append( + ContentToolResponseCodeExecution( + output=output, + error=error, + container_id=completion.container.id + if completion.container + else None, + extra=extra, + ) + ) return AssistantTurn( contents, diff --git a/tests/test_provider_anthropic.py b/tests/test_provider_anthropic.py index 208e8508..93236070 100644 --- a/tests/test_provider_anthropic.py +++ b/tests/test_provider_anthropic.py @@ -7,6 +7,7 @@ ChatAnthropic, UserTurn, content_image_file, + tool_code_execution, tool_web_fetch, tool_web_search, ) @@ -371,3 +372,136 @@ class Person(BaseModel): output_config = args["output_config"] assert output_config["effort"] == "high" assert output_config["format"]["type"] == "json_schema" + + +def test_anthropic_code_execution_parses_request_and_response(): + """server_tool_use(code_execution) + code_execution_tool_result parse correctly.""" + from anthropic.types import Message + from chatlas._content import ( + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, + ) + from chatlas._provider_anthropic import AnthropicProvider + + chat = ChatAnthropic() + provider = chat.provider + assert isinstance(provider, AnthropicProvider) + + message = Message.model_validate( + { + "id": "msg_1", + "type": "message", + "role": "assistant", + "model": "claude-opus-4-6", + "content": [ + { + "type": "server_tool_use", + "id": "srvtoolu_1", + "name": "code_execution", + "input": {"code": "print(1 + 1)"}, + }, + { + "type": "code_execution_tool_result", + "tool_use_id": "srvtoolu_1", + "content": { + "type": "code_execution_result", + "stdout": "2\n", + "stderr": "", + "return_code": 0, + "content": [], + }, + }, + ], + "stop_reason": "end_turn", + "usage": {"input_tokens": 10, "output_tokens": 5}, + "container": { + "id": "cntr_xyz", + "expires_at": "2026-07-21T00:00:00Z", + }, + } + ) + + turn = provider._as_turn(message, has_data_model=False) + + assert len(turn.contents) == 2 + request = turn.contents[0] + assert isinstance(request, ContentToolRequestCodeExecution) + assert request.code == "print(1 + 1)" + + response = turn.contents[1] + assert isinstance(response, ContentToolResponseCodeExecution) + assert response.output == "2\n" + assert response.error is None + assert response.container_id == "cntr_xyz" + + +def test_anthropic_code_execution_error_result(): + """An error result maps to `error`, not `output`.""" + from anthropic.types import Message + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._provider_anthropic import AnthropicProvider + + chat = ChatAnthropic() + provider = chat.provider + assert isinstance(provider, AnthropicProvider) + + message = Message.model_validate( + { + "id": "msg_1", + "type": "message", + "role": "assistant", + "model": "claude-opus-4-6", + "content": [ + { + "type": "code_execution_tool_result", + "tool_use_id": "srvtoolu_1", + "content": { + "type": "code_execution_tool_result_error", + "error_code": "execution_time_exceeded", + }, + }, + ], + "stop_reason": "end_turn", + "usage": {"input_tokens": 10, "output_tokens": 5}, + } + ) + + turn = provider._as_turn(message, has_data_model=False) + response = turn.contents[0] + assert isinstance(response, ContentToolResponseCodeExecution) + assert response.output is None + assert response.error == "execution_time_exceeded" + assert response.container_id is None + + +def test_anthropic_code_execution_tool_schema(): + from chatlas._provider_anthropic import AnthropicProvider + + schema = AnthropicProvider._anthropic_tool_schema(tool_code_execution()) + assert schema["name"] == "code_execution" + assert schema["type"] == "code_execution_20260521" + + +def test_anthropic_code_execution_container_reuse(): + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._provider_anthropic import AnthropicProvider + from chatlas._turn import AssistantTurn + + chat = ChatAnthropic() + chat.register_tool(tool_code_execution()) + provider = chat.provider + assert isinstance(provider, AnthropicProvider) + + turns = [ + AssistantTurn( + [ContentToolResponseCodeExecution(output="1", container_id="cntr_xyz")] + ) + ] + kwargs = provider._chat_perform_args( + stream=False, + turns=turns, + tools=chat._tools, # type: ignore[reportPrivateUsage] + data_model=None, + kwargs=None, + ) + assert kwargs["container"] == "cntr_xyz" From 6874b7ec30f9d9c273fbcbb66bf964093cb5e5dc Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 16:46:53 -0500 Subject: [PATCH 6/9] feat: support code execution tool in Google provider --- chatlas/_provider_google.py | 49 +++++++++++- tests/test_provider_google.py | 138 +++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/chatlas/_provider_google.py b/chatlas/_provider_google.py index bdafdc15..a9af587b 100644 --- a/chatlas/_provider_google.py +++ b/chatlas/_provider_google.py @@ -19,6 +19,8 @@ ContentThinking, ContentThinkingDelta, ContentToolRequest, + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, ContentToolResult, ) from ._logging import log_model_default @@ -32,7 +34,7 @@ ) from ._tokens import get_price_info from ._tools import Tool, ToolBuiltIn -from ._tools_builtin import ToolWebFetch, ToolWebSearch +from ._tools_builtin import ToolCodeExecution, ToolWebFetch, ToolWebSearch from ._turn import AssistantTurn, FinishReason, SystemTurn, Turn, UserTurn, user_turn if TYPE_CHECKING: @@ -398,6 +400,9 @@ def _chat_perform_args( elif isinstance(tool, ToolWebFetch): gtool = GoogleTool(url_context=tool.get_definition("google")) google_tools.append(gtool) + elif isinstance(tool, ToolCodeExecution): + gtool = GoogleTool(code_execution=tool.get_definition("google")) + google_tools.append(gtool) elif isinstance(tool, ToolBuiltIn): gtool = GoogleTool.model_validate(tool.definition) google_tools.append(gtool) @@ -618,6 +623,26 @@ def _as_part_type(self, content: Content) -> "Part": response=resp, ) ) + elif isinstance(content, ContentToolRequestCodeExecution): + from google.genai.types import ExecutableCode, Language + + return Part( + executable_code=ExecutableCode( + code=content.code, + language=Language.PYTHON, + ) + ) + elif isinstance(content, ContentToolResponseCodeExecution): + from google.genai.types import CodeExecutionResult, Outcome + + return Part( + code_execution_result=CodeExecutionResult( + outcome=Outcome.OUTCOME_FAILED + if content.error + else Outcome.OUTCOME_OK, + output=content.error or content.output, + ) + ) raise ValueError(f"Unknown content type: {type(content)}") def _as_turn( @@ -695,6 +720,28 @@ def _as_turn( image_content_type=mime_type, # type: ignore ) ) + executable_code = part.get("executable_code") + if executable_code: + code = executable_code.get("code") + if code: + contents.append( + ContentToolRequestCodeExecution( + code=code, + language=executable_code.get("language"), + extra=dict(executable_code), + ) + ) + code_execution_result = part.get("code_execution_result") + if code_execution_result: + outcome = code_execution_result.get("outcome") + output = code_execution_result.get("output") + contents.append( + ContentToolResponseCodeExecution( + output=output if outcome == "OUTCOME_OK" else None, + error=output if outcome != "OUTCOME_OK" else None, + extra=dict(code_execution_result), + ) + ) if isinstance(finish_reason, FinishReason): finish_reason = finish_reason.name diff --git a/tests/test_provider_google.py b/tests/test_provider_google.py index 4d12fcab..64f559ff 100644 --- a/tests/test_provider_google.py +++ b/tests/test_provider_google.py @@ -1,6 +1,12 @@ import pytest import requests -from chatlas import ChatGoogle, ChatVertex, tool_web_fetch, tool_web_search +from chatlas import ( + ChatGoogle, + ChatVertex, + tool_code_execution, + tool_web_fetch, + tool_web_search, +) from chatlas._provider_google import ( normalize_finish_reason as google_normalize_finish_reason, ) @@ -602,3 +608,133 @@ def test_google_tool_config_not_set_when_tools_not_mixed(): only_builtin = {"web_search": tool_web_search()} kwargs = provider._chat_perform_args(turns=[user_turn("hi")], tools=only_builtin) assert kwargs["config"].tool_config is None + + +def test_google_code_execution_parses_request_and_response(): + """executable_code + code_execution_result parts parse correctly.""" + from chatlas._content import ( + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, + ) + from chatlas._provider_google import GoogleProvider + + provider = GoogleProvider( + model="gemini-2.5-flash-preview-04-17", + api_key="dummy", + kwargs=None, + ) + + message = { + "candidates": [ + { + "content": { + "parts": [ + { + "executable_code": { + "code": "print(1 + 1)", + "language": "PYTHON", + } + }, + { + "code_execution_result": { + "outcome": "OUTCOME_OK", + "output": "2\n", + } + }, + ] + }, + "finish_reason": "STOP", + } + ], + } + + turn = provider._as_turn(message, has_data_model=False) + assert len(turn.contents) == 2 + + request = turn.contents[0] + assert isinstance(request, ContentToolRequestCodeExecution) + assert request.code == "print(1 + 1)" + assert request.language == "PYTHON" + + response = turn.contents[1] + assert isinstance(response, ContentToolResponseCodeExecution) + assert response.output == "2\n" + assert response.error is None + + +def test_google_code_execution_failed_outcome_maps_to_error(): + from chatlas._content import ContentToolResponseCodeExecution + from chatlas._provider_google import GoogleProvider + + provider = GoogleProvider( + model="gemini-2.5-flash-preview-04-17", + api_key="dummy", + kwargs=None, + ) + + message = { + "candidates": [ + { + "content": { + "parts": [ + { + "code_execution_result": { + "outcome": "OUTCOME_FAILED", + "output": "NameError: x is not defined", + } + }, + ] + }, + "finish_reason": "STOP", + } + ], + } + + turn = provider._as_turn(message, has_data_model=False) + response = turn.contents[0] + assert isinstance(response, ContentToolResponseCodeExecution) + assert response.output is None + assert response.error == "NameError: x is not defined" + + +def test_google_code_execution_round_trip(): + from chatlas._content import ( + ContentToolRequestCodeExecution, + ContentToolResponseCodeExecution, + ) + from chatlas._provider_google import GoogleProvider + + provider = GoogleProvider( + model="gemini-2.5-flash-preview-04-17", + api_key="dummy", + kwargs=None, + ) + + request = ContentToolRequestCodeExecution(code="print(1 + 1)", language="PYTHON") + part = provider._as_part_type(request) + assert part.executable_code is not None + assert part.executable_code.code == "print(1 + 1)" + + response = ContentToolResponseCodeExecution(output="2\n") + part = provider._as_part_type(response) + assert part.code_execution_result is not None + assert part.code_execution_result.output == "2\n" + + +def test_google_code_execution_tool_definition_registered(): + from chatlas._provider_google import GoogleProvider + + provider = GoogleProvider( + model="gemini-2.5-flash-preview-04-17", + api_key="dummy", + kwargs=None, + ) + kwargs = provider._chat_perform_args( + turns=[], + tools={"code_execution": tool_code_execution()}, + data_model=None, + kwargs=None, + ) + tools = kwargs["config"].tools + assert tools is not None + assert tools[0].code_execution is not None From 375928d251c590c6f9fca6fa00fd0d1bfd444ae2 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 17:11:59 -0500 Subject: [PATCH 7/9] test: add code execution VCR tests and docs 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. --- chatlas/_tools_builtin.py | 21 +++++++++++--------- docs/_quarto.yml | 3 ++- docs/get-started/tools.qmd | 25 ++++++++++++++++++++++++ tests/conftest.py | 33 ++++++++++++++++++++++++++++++++ tests/test_provider_anthropic.py | 25 +++++++++++++++++++++++- tests/test_provider_google.py | 7 +++++++ tests/test_provider_openai.py | 18 +++++++++++++++++ tests/test_tools_builtin.py | 2 +- 8 files changed, 122 insertions(+), 12 deletions(-) diff --git a/chatlas/_tools_builtin.py b/chatlas/_tools_builtin.py index e1ae8180..852a4baa 100644 --- a/chatlas/_tools_builtin.py +++ b/chatlas/_tools_builtin.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from anthropic.types import WebSearchTool20250305Param from anthropic.types.beta import ( - BetaCodeExecutionTool20260521Param, + BetaCodeExecutionTool20250522Param, BetaWebFetchTool20250910Param, ) from anthropic.types.beta.beta_citations_config_param import ( @@ -687,7 +687,7 @@ def get_definition( def get_definition( self, provider_name: Literal["anthropic"], - ) -> "BetaCodeExecutionTool20260521Param": ... + ) -> "BetaCodeExecutionTool20250522Param": ... @overload def get_definition( @@ -702,7 +702,7 @@ def get_definition( # OpenAI-specific container_id: Optional[str] = None, ) -> ( - "CodeInterpreter | BetaCodeExecutionTool20260521Param | GoogleToolCodeExecution" + "CodeInterpreter | BetaCodeExecutionTool20250522Param | GoogleToolCodeExecution" ): """ Get the provider-specific tool definition. @@ -742,12 +742,12 @@ def _openai_definition(*, container_id: Optional[str] = None) -> "CodeInterprete return {"type": "code_interpreter", "container": container} # type: ignore @staticmethod - def _anthropic_definition() -> "BetaCodeExecutionTool20260521Param": + def _anthropic_definition() -> "BetaCodeExecutionTool20250522Param": """Generate Anthropic/Claude code execution tool definition.""" # https://docs.claude.com/en/docs/agents-and-tools/tool-use/code-execution-tool return { "name": "code_execution", - "type": "code_execution_20260521", + "type": "code_execution_20250522", } @staticmethod @@ -774,7 +774,7 @@ def tool_code_execution() -> ToolCodeExecution: ------------- - **OpenAI**: Code execution is available by default with the Responses API. - **Claude**: The code execution tool requires the beta header - `anthropic-beta: code-execution-2026-05-21`. Pass this via the `kwargs` + `anthropic-beta: code-execution-2025-05-22`. Pass this via the `kwargs` parameter's `default_headers` option (see examples below). - **Google**: Code execution is available by default with Gemini. @@ -797,7 +797,7 @@ def tool_code_execution() -> ToolCodeExecution: from chatlas import ChatAnthropic, tool_code_execution chat = ChatAnthropic( - kwargs={"default_headers": {"anthropic-beta": "code-execution-2026-05-21"}} + kwargs={"default_headers": {"anthropic-beta": "code-execution-2025-05-22"}} ) chat.register_tool(tool_code_execution()) chat.chat("What's the 20th Fibonacci number?") @@ -810,9 +810,12 @@ def tool_code_execution() -> ToolCodeExecution: CSVs) aren't downloaded or decoded -- their raw provider references are still available via each content's `extra` attribute. - OpenAI and Claude reuse the same sandbox across turns in a conversation + OpenAI reuses the same sandbox across turns in a conversation automatically, so a variable defined in one turn is still available in - the next. Google starts a fresh sandbox on every turn. + the next. Claude also reuses the same container across turns, but its + code execution tool does not persist Python interpreter state (e.g. + variables) between separate executions -- each execution starts fresh. + Google starts a fresh sandbox on every turn. """ return ToolCodeExecution() diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 61b43e3a..d45e8cb0 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -184,10 +184,11 @@ quartodoc: - Tool - ToolRejectError - title: Built-in tools - desc: Provider-agnostic access to built-in web search and fetch capabilities. + desc: Provider-agnostic access to built-in web search, fetch, and code execution capabilities. contents: - tool_web_search - tool_web_fetch + - tool_code_execution - title: Parallel and batch chat desc: Submit multiple chats in parallel (fast) or one batch (cheap) contents: diff --git a/docs/get-started/tools.qmd b/docs/get-started/tools.qmd index a4e2f3ee..73114436 100644 --- a/docs/get-started/tools.qmd +++ b/docs/get-started/tools.qmd @@ -110,6 +110,31 @@ See the [MCP tools guide](../misc/mcp-tools.qmd) for more information. ::: +### Code execution + +Some providers can run code for you in a server-side sandbox -- useful for +math, data analysis, or letting the model verify its own logic. Register it +the same way as web search or fetch: + +```python +from chatlas import ChatAnthropic, tool_code_execution + +chat = ChatAnthropic( + kwargs={"default_headers": {"anthropic-beta": "code-execution-2025-05-22"}} +) +chat.register_tool(tool_code_execution()) +chat.chat("What's the 20th Fibonacci number?") +``` + +Supported providers: OpenAI, Claude (Anthropic), Google (Gemini). Only text +output (stdout/stderr) is surfaced -- files the code produces (e.g. plots) +aren't downloaded automatically. OpenAI reuses the same sandbox across turns +in a conversation, so state (like a variable) persists. Claude also reuses +the same container across turns, but doesn't persist Python interpreter +state between executions -- each execution starts fresh. Google starts a +fresh sandbox every turn too. + + ### Tool errors When a tool function is called, it may fail for various reasons, such as network issues, invalid input, or unexpected exceptions. diff --git a/tests/conftest.py b/tests/conftest.py index 6bfcacaf..bdf72282 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -417,6 +417,39 @@ def assert_tool_web_search(chat_fun: ChatFun, tool, hint: str = "", stream: bool assert "May" in str(response) +def assert_tool_code_execution(chat_fun: ChatFun, tool, stream: bool = True): + """Test code execution tool functionality.""" + chat = chat_fun() + chat.register_tool(tool) + + response = chat.chat( + "Use code execution to compute the 20th Fibonacci number " + "(0-indexed, so fib(0) = 0, fib(1) = 1). Just give me the number.", + stream=stream, + ) + assert "6765" in str(response) + + +def assert_tool_code_execution_persistence( + chat_fun: ChatFun, tool, stream: bool = True +): + """Test that the sandbox persists a variable across turns.""" + chat = chat_fun() + chat.register_tool(tool) + + chat.chat( + "Use code execution to define a variable `x = 42`. " + "Don't print anything yet.", + stream=stream, + ) + response = chat.chat( + "Now use code execution to print `x * 2`, reusing the same `x` " + "from before -- don't redefine it.", + stream=stream, + ) + assert "84" in str(response) + + retry_api_call = retry( wait=wait_exponential(min=1, max=60), stop=stop_after_attempt(3), diff --git a/tests/test_provider_anthropic.py b/tests/test_provider_anthropic.py index 93236070..ec5004d6 100644 --- a/tests/test_provider_anthropic.py +++ b/tests/test_provider_anthropic.py @@ -23,6 +23,7 @@ assert_images_remote, assert_list_models, assert_pdf_local, + assert_tool_code_execution, assert_tool_web_fetch, assert_tool_web_search, assert_tools_async, @@ -163,6 +164,28 @@ def test_anthropic_web_search_citations(): assert has_citations, "Expected citations on text blocks from web search" +@pytest.mark.vcr +def test_anthropic_code_execution(): + def chat_fun(**kwargs): + return ChatAnthropic( + kwargs={ + "default_headers": {"anthropic-beta": "code-execution-2025-05-22"} + }, + **kwargs, + ) + + assert_tool_code_execution(chat_fun, tool_code_execution()) + + +# N.B. no code execution *persistence* test for Anthropic: despite chatlas +# reusing the same `container` across turns, Claude's code execution tool +# does not persist Python REPL state (e.g. variables) across separate turns +# -- each execution starts a fresh interpreter. This was verified live and +# contradicts the tool's own SDK docstring ("REPL state persistence"), so +# it's treated the same as Google's documented per-turn-fresh-sandbox +# limitation rather than a bug in chatlas. + + @pytest.mark.vcr def test_data_extraction(): assert_data_extraction(chat_func) @@ -479,7 +502,7 @@ def test_anthropic_code_execution_tool_schema(): schema = AnthropicProvider._anthropic_tool_schema(tool_code_execution()) assert schema["name"] == "code_execution" - assert schema["type"] == "code_execution_20260521" + assert schema["type"] == "code_execution_20250522" def test_anthropic_code_execution_container_reuse(): diff --git a/tests/test_provider_google.py b/tests/test_provider_google.py index 64f559ff..95a562de 100644 --- a/tests/test_provider_google.py +++ b/tests/test_provider_google.py @@ -19,6 +19,7 @@ assert_images_remote_error, assert_list_models, assert_pdf_local, + assert_tool_code_execution, assert_tool_web_fetch, assert_tool_web_search, assert_tools_parallel, @@ -215,6 +216,12 @@ def test_google_web_search(): assert_tool_web_search(chat_func, tool_web_search()) +@pytest.mark.vcr +@retry_gemini_call +def test_google_code_execution(): + assert_tool_code_execution(chat_func, tool_code_execution()) + + @pytest.mark.vcr @retry_gemini_call def test_images_inline(): diff --git a/tests/test_provider_openai.py b/tests/test_provider_openai.py index 53a5f082..41d8d3de 100644 --- a/tests/test_provider_openai.py +++ b/tests/test_provider_openai.py @@ -14,6 +14,8 @@ assert_images_remote, assert_list_models, assert_pdf_local, + assert_tool_code_execution, + assert_tool_code_execution_persistence, assert_tool_web_search, assert_tools_async, assert_tools_parallel, @@ -130,6 +132,22 @@ def chat_fun(**kwargs): ) +@pytest.mark.vcr +def test_openai_code_execution(): + def chat_fun(**kwargs): + return ChatOpenAI(model="gpt-4.1", **kwargs) + + assert_tool_code_execution(chat_fun, tool_code_execution()) + + +@pytest.mark.vcr +def test_openai_code_execution_persistence(): + def chat_fun(**kwargs): + return ChatOpenAI(model="gpt-4.1", **kwargs) + + assert_tool_code_execution_persistence(chat_fun, tool_code_execution()) + + @pytest.mark.vcr def test_openai_images(): chat_fun = ChatOpenAI diff --git a/tests/test_tools_builtin.py b/tests/test_tools_builtin.py index b261e963..6863237c 100644 --- a/tests/test_tools_builtin.py +++ b/tests/test_tools_builtin.py @@ -410,7 +410,7 @@ def test_anthropic_definition(self): tool = tool_code_execution() definition = tool.get_definition("anthropic") assert definition["name"] == "code_execution" - assert definition["type"] == "code_execution_20260521" + assert definition["type"] == "code_execution_20250522" def test_google_definition(self): from google.genai.types import ToolCodeExecution as GoogleToolCodeExecution From 7be5e7491ac1c323f1998e809c6e584db1671c25 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 17:12:05 -0500 Subject: [PATCH 8/9] test: record VCR cassettes for code execution tests Recorded against live OpenAI, Anthropic, and Google APIs; scanned clean for secrets by scripts/check_vcr_secrets.py. --- .../test_anthropic_code_execution.yaml | 175 ++++ .../test_google_code_execution.yaml | 80 ++ .../test_openai_code_execution.yaml | 472 +++++++++++ ...est_openai_code_execution_persistence.yaml | 761 ++++++++++++++++++ 4 files changed, 1488 insertions(+) create mode 100644 tests/_vcr/test_provider_anthropic/test_anthropic_code_execution.yaml create mode 100644 tests/_vcr/test_provider_google/test_google_code_execution.yaml create mode 100644 tests/_vcr/test_provider_openai/test_openai_code_execution.yaml create mode 100644 tests/_vcr/test_provider_openai/test_openai_code_execution_persistence.yaml diff --git a/tests/_vcr/test_provider_anthropic/test_anthropic_code_execution.yaml b/tests/_vcr/test_provider_anthropic/test_anthropic_code_execution.yaml new file mode 100644 index 00000000..e2fe24cf --- /dev/null +++ b/tests/_vcr/test_provider_anthropic/test_anthropic_code_execution.yaml @@ -0,0 +1,175 @@ +interactions: +- request: + body: '{"max_tokens": 4096, "messages": [{"role": "user", "content": [{"text": + "Use code execution to compute the 20th Fibonacci number (0-indexed, so fib(0) + = 0, fib(1) = 1). Just give me the number.", "type": "text", "cache_control": + {"type": "ephemeral", "ttl": "5m"}}]}], "model": "claude-sonnet-4-6", "stream": + true, "tools": [{"name": "code_execution", "type": "code_execution_20250522"}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + anthropic-beta: + - code-execution-2025-05-22 + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '365' + content-type: + - application/json + host: + - api.anthropic.com + x-stainless-async: + - 'false' + x-stainless-read-timeout: + - '600' + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: 'event: message_start + + data: {"type":"message_start","message":{"model":"claude-sonnet-4-6","id":"msg_011CdFxG4dasFq1CME8FgDso","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":970,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":65,"service_tier":"standard","inference_geo":"global"}} } + + + event: content_block_start + + data: {"type":"content_block_start","index":0,"content_block":{"type":"server_tool_use","id":"srvtoolu_018dAxW3UzAfH8W1pSrwuhbD","name":"code_execution","input":{}} } + + + event: ping + + data: {"type": "ping"} + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"code\": + \"\\ndef fib(n):\\n a, b = 0, 1\\n for _ in range(n):\\n a, b + = b"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":", + a + b\\n return a\\n\\nprint(fib(20))\\n"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"\"}"} } + + + event: content_block_stop + + data: {"type":"content_block_stop","index":0 } + + + event: content_block_start + + data: {"type":"content_block_start","index":1,"content_block":{"type":"code_execution_tool_result","tool_use_id":"srvtoolu_018dAxW3UzAfH8W1pSrwuhbD","content":{"type":"code_execution_result","stdout":"6765","stderr":"","return_code":0,"content":[],"abort_reason":null}} } + + + event: content_block_stop + + data: {"type":"content_block_stop","index":1 } + + + event: content_block_start + + data: {"type":"content_block_start","index":2,"content_block":{"type":"text","text":""}} + + + event: content_block_delta + + data: {"type":"content_block_delta","index":2,"delta":{"type":"text_delta","text":"The + 20th Fibonacci number is"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":2,"delta":{"type":"text_delta","text":" + **6765**."} } + + + event: content_block_stop + + data: {"type":"content_block_stop","index":2 } + + + event: message_delta + + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"container":{"id":"container_01BRzMk5ucXoE1VG3XWHRPfK","expires_at":"2026-07-21T22:59:36.338608Z"}},"usage":{"input_tokens":971,"cache_creation_input_tokens":1091,"cache_read_input_tokens":0,"output_tokens":119,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0}} } + + + event: message_stop + + data: {"type":"message_stop" } + + + ' + headers: + anthropic-ratelimit-input-tokens-limit: + - '10000000' + anthropic-ratelimit-input-tokens-remaining: + - '9999000' + anthropic-ratelimit-input-tokens-reset: + - '2026-07-21T21:59:28Z' + anthropic-ratelimit-output-tokens-limit: + - '2000000' + anthropic-ratelimit-output-tokens-remaining: + - '2000000' + anthropic-ratelimit-output-tokens-reset: + - '2026-07-21T21:59:28Z' + anthropic-ratelimit-requests-limit: + - '20000' + anthropic-ratelimit-requests-remaining: + - '19999' + anthropic-ratelimit-requests-reset: + - '2026-07-21T21:59:28Z' + anthropic-ratelimit-tokens-limit: + - '12000000' + anthropic-ratelimit-tokens-remaining: + - '11999000' + anthropic-ratelimit-tokens-reset: + - '2026-07-21T21:59:28Z' + cache-control: + - no-cache + cf-cache-status: + - DYNAMIC + connection: + - keep-alive + content-length: + - '2824' + content-security-policy: + - default-src 'none'; frame-ancestors 'none' + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 21 Jul 2026 21:59:29 GMT + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + traceresponse: + - 00-aa7c3fc4448e6f4cf3c9e8f1208d2cfb-49d9bcbc3347ab44-01 + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-robots-tag: + - none + status: + code: 200 + message: OK +version: 1 diff --git a/tests/_vcr/test_provider_google/test_google_code_execution.yaml b/tests/_vcr/test_provider_google/test_google_code_execution.yaml new file mode 100644 index 00000000..556c1e83 --- /dev/null +++ b/tests/_vcr/test_provider_google/test_google_code_execution.yaml @@ -0,0 +1,80 @@ +interactions: +- request: + body: '{"contents": [{"parts": [{"text": "Use code execution to compute the 20th + Fibonacci number (0-indexed, so fib(0) = 0, fib(1) = 1). Just give me the number."}], + "role": "user"}], "tools": [{"codeExecution": {}}], "generationConfig": {"temperature": + 0}}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '251' + content-type: + - application/json + host: + - generativelanguage.googleapis.com + x-goog-api-client: + - google-genai-sdk/2.13.0 gl-python/3.12.13 + method: POST + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:streamGenerateContent?alt=sse + response: + body: + string: "data: {\"candidates\": [{\"content\": {\"parts\": [{\"executableCode\": + {\"language\": \"PYTHON\",\"code\": \"def fib(n):\\n a, b = 0, 1\\n for + _ in range(n):\\n a, b = b, a + b\\n return a\\n\\nprint(fib(20))\\n\",\"id\": + \"2ydsz5r8\"},\"thoughtSignature\": \"EtEDCs4DARFNMg9yXq63pfAU+5EIHV1gUziVmbRPItM89YwjikWVR+GszBzKz0NBHKG6dfDNmNYOlcsGgMACcXqhbj/t6pLQ6eAEe1a8BzO0AavIIP37l6lsap9ihFyDLtgnPx/sHCcSG/5efbQZigZZc+bGZVcymO3JSO2Y4KUx8ZUdVVcpY8wFJs4U1qa8v4UVReoTvdfnyBQVGqnHakAfrh6soDHsX0jEsm+LGU+n6mdFlS54KT+jUgwtbAvxy1p1p9Oc83LshluQqZSTIdF6930VrAJMA+a50nXx3g68LrIQ2glKl/rDdzupfNC/U0nUryFUAHYPlE9wwVDptLBZEMmm4iBBzu3cPxxtsOQ35neqB7tZrhZ2n/gNwx9BgUTmiWtpnsMK8Xv80N571ikBeXruGxoaUWiMJctm8BdP6DRxo9b/o9M5WzKS7Fw+j+nlGbH35lDG7wXxNMLpPDUPW6aZqSxLNxS6FoylUn+J1L+n66CDJFSRM+YQHKjja7XexCBsH0uqyVTwhsn2xeDRihhBTBabGAdOJs+pr7HFTtLCtbKp4YJHLZj34xZ2Ch1lkWC6VJ3Kfjoi85vudBypP7pzuvxf57GxqdEz8UrmFojP\"}],\"role\": + \"model\"},\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 41,\"candidatesTokenCount\": + 62,\"totalTokenCount\": 219,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": + 41}],\"thoughtsTokenCount\": 116,\"serviceTier\": \"standard\"},\"modelVersion\": + \"gemini-3.5-flash\",\"responseId\": \"F-1fapjUPPzN-8YPr4a9mQ8\"}\r\n\r\ndata: + {\"candidates\": [{\"content\": {\"parts\": [{\"codeExecutionResult\": {\"outcome\": + \"OUTCOME_OK\",\"output\": \"6765\\n\",\"id\": \"2ydsz5r8\"}}],\"role\": \"model\"}}],\"usageMetadata\": + {\"promptTokenCount\": 41,\"totalTokenCount\": 41,\"promptTokensDetails\": + [{\"modality\": \"TEXT\",\"tokenCount\": 41}],\"serviceTier\": \"standard\"},\"modelVersion\": + \"gemini-3.5-flash\",\"responseId\": \"F-1fapjUPPzN-8YPr4a9mQ8\"}\r\n\r\ndata: + {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \"6765\"}],\"role\": + \"model\"},\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 41,\"candidatesTokenCount\": + 4,\"totalTokenCount\": 82,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": + 41}],\"thoughtsTokenCount\": 37,\"serviceTier\": \"standard\"},\"modelVersion\": + \"gemini-3.5-flash\",\"responseId\": \"F-1fapjUPPzN-8YPr4a9mQ8\"}\r\n\r\ndata: + {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \"\",\"thoughtSignature\": + \"Er8BCrwBARFNMg+sMxy77lRN7eSUv8M2cz7iRPN8mgg/5TNPkYX5gp+mG0Y04XPSfSOs3v6p/0W6wmvbCq8ZEg/f0Q/57f5P5bPIhDoIOo9sIdTZSzBf55lu6gQLsPbF5AJQBGdD4Q+NgR+SAt9Zrb/3hTBjkhFDAU3qlwLlygeS84fYD+8ESgKch32f3Pu4tkkfBK067o/faYQSOoxV45AC+qiRqcjHxPXOkcQqwBnL0rk+rG3M55v4WnuZbdRkGmI=\"}],\"role\": + \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": + 157,\"candidatesTokenCount\": 66,\"totalTokenCount\": 598,\"promptTokensDetails\": + [{\"modality\": \"TEXT\",\"tokenCount\": 157}],\"toolUsePromptTokenCount\": + 222,\"toolUsePromptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": + 222}],\"thoughtsTokenCount\": 153,\"serviceTier\": \"standard\"},\"modelVersion\": + \"gemini-3.5-flash\",\"responseId\": \"F-1fapjUPPzN-8YPr4a9mQ8\"}\r\n\r\n" + headers: + alt-svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + content-disposition: + - attachment + content-type: + - text/event-stream + date: + - Tue, 21 Jul 2026 22:05:13 GMT + server: + - scaffolding on HTTPServer2 + server-timing: + - gfet4t7; dur=1306 + transfer-encoding: + - chunked + vary: + - Origin + - X-Origin + - Referer + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-xss-protection: + - '0' + status: + code: 200 + message: OK +version: 1 diff --git a/tests/_vcr/test_provider_openai/test_openai_code_execution.yaml b/tests/_vcr/test_provider_openai/test_openai_code_execution.yaml new file mode 100644 index 00000000..7bffe5df --- /dev/null +++ b/tests/_vcr/test_provider_openai/test_openai_code_execution.yaml @@ -0,0 +1,472 @@ +interactions: +- request: + body: '{"input": [{"role": "user", "content": [{"type": "input_text", "text": + "Use code execution to compute the 20th Fibonacci number (0-indexed, so fib(0) + = 0, fib(1) = 1). Just give me the number."}]}], "model": "gpt-4.1", "store": + false, "stream": true, "tools": [{"type": "code_interpreter", "container": {"type": + "auto"}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '303' + content-type: + - application/json + host: + - api.openai.com + x-stainless-async: + - 'false' + x-stainless-read-timeout: + - '600' + method: POST + uri: https://api.openai.com/v1/responses + response: + body: + string: 'event: response.created + + data: {"type":"response.created","response":{"id":"resp_050aaa8793a4eddc016a5fea29a7988194b29400d594354572","object":"response","created_at":1784670761,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + + event: response.in_progress + + data: {"type":"response.in_progress","response":{"id":"resp_050aaa8793a4eddc016a5fea29a7988194b29400d594354572","object":"response","created_at":1784670761,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + + event: response.output_item.added + + data: {"type":"response.output_item.added","item":{"id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","type":"code_interpreter_call","status":"in_progress","code":"","container_id":"cntr_6a5fea2aff5c8193a1065b8c97a102160cefcd69266ffa1c","outputs":null},"output_index":0,"sequence_number":2} + + + event: response.code_interpreter_call.in_progress + + data: {"type":"response.code_interpreter_call.in_progress","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","output_index":0,"sequence_number":3} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"def","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"kRM4LyFV6qeUv","output_index":0,"sequence_number":4} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" fib","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"fe8sWC7fDW45","output_index":0,"sequence_number":5} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"(n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"2wca7B7uZEItSl","output_index":0,"sequence_number":6} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"):\r\n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"ZDx4UP4pchSQ","output_index":0,"sequence_number":7} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"VSWqjSvEe6PMM","output_index":0,"sequence_number":8} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" a","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"vP4OnNncePl94A","output_index":0,"sequence_number":9} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":",","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"hfFxh7XJF1pOVXz","output_index":0,"sequence_number":10} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" b","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"o2dDxqMydCip9M","output_index":0,"sequence_number":11} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" =","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"9SA7XKY4BRuCN7","output_index":0,"sequence_number":12} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"epeRdmvuvwDbcLl","output_index":0,"sequence_number":13} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"0","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"gNawHfa2gKzhsnY","output_index":0,"sequence_number":14} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":",","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"6USMYJFqXToDDno","output_index":0,"sequence_number":15} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"ipMSRiKrpaHPwl0","output_index":0,"sequence_number":16} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"1","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"HX2ZHSh3jO1rVlm","output_index":0,"sequence_number":17} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"\r\n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"iDObfsgVnNY967","output_index":0,"sequence_number":18} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"bnVFGjrBhvXUe","output_index":0,"sequence_number":19} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" for","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"AmB4tU3FaEwz","output_index":0,"sequence_number":20} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" _","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"yrtwYE7YtkZqx4","output_index":0,"sequence_number":21} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" in","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"4pBUv5NHRQfcm","output_index":0,"sequence_number":22} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" range","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"iFywOIcNQK","output_index":0,"sequence_number":23} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"(n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"J2RqWrfGTzRn33","output_index":0,"sequence_number":24} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"):\r\n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"4ylQrSA8j80v","output_index":0,"sequence_number":25} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"bjqbciwZC","output_index":0,"sequence_number":26} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" a","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"nwBGIEHvy86fN6","output_index":0,"sequence_number":27} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":",","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"CChxTJw4svAkYDV","output_index":0,"sequence_number":28} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" b","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"0s4Mn31khjq9UF","output_index":0,"sequence_number":29} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" =","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"fiwcTEhvPmtd97","output_index":0,"sequence_number":30} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" b","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"vq8Wdv3Qe8Fuve","output_index":0,"sequence_number":31} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":",","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"MQof3HLPYGM2Wwz","output_index":0,"sequence_number":32} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" a","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"zpHV8y2ncbMum0","output_index":0,"sequence_number":33} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" +","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"tYP1qoxChCq40P","output_index":0,"sequence_number":34} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" b","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"svO0zOmGtozLtS","output_index":0,"sequence_number":35} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"\r\n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"Vj0FMOCXiXTlhv","output_index":0,"sequence_number":36} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"4qTAqBQnzA0GX","output_index":0,"sequence_number":37} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" return","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"Q0drPtMPo","output_index":0,"sequence_number":38} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" a","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"wGxeVkSFmyyKAd","output_index":0,"sequence_number":39} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"\r\n\r\n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"LcUz4FUCUT3x","output_index":0,"sequence_number":40} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"fib","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"qIUXL9NOSdZnH","output_index":0,"sequence_number":41} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"_","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"EPScMkbXAStT6xD","output_index":0,"sequence_number":42} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"20","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"OvHnNPApV2Gxwk","output_index":0,"sequence_number":43} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" =","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"5yGOcA2IH2xZNv","output_index":0,"sequence_number":44} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" fib","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"uf7faDr2sXk7","output_index":0,"sequence_number":45} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"(","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"jb5T433BNb3se3J","output_index":0,"sequence_number":46} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"20","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"xQGiWd02oSaRxy","output_index":0,"sequence_number":47} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":")\r\n","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"XMR9S384Jdnjw","output_index":0,"sequence_number":48} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"fib","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"a9wymtHSPQ2NC","output_index":0,"sequence_number":49} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"_","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"28gv1aaselBGhZv","output_index":0,"sequence_number":50} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"20","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","obfuscation":"WstJbkyJvcZ9q4","output_index":0,"sequence_number":51} + + + event: response.code_interpreter_call_code.done + + data: {"type":"response.code_interpreter_call_code.done","code":"def fib(n):\r\n a, + b = 0, 1\r\n for _ in range(n):\r\n a, b = b, a + b\r\n return + a\r\n\r\nfib_20 = fib(20)\r\nfib_20","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","output_index":0,"sequence_number":52} + + + event: response.code_interpreter_call.interpreting + + data: {"type":"response.code_interpreter_call.interpreting","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","output_index":0,"sequence_number":53} + + + event: response.code_interpreter_call.completed + + data: {"type":"response.code_interpreter_call.completed","item_id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","output_index":0,"sequence_number":54} + + + event: response.output_item.done + + data: {"type":"response.output_item.done","item":{"id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","type":"code_interpreter_call","status":"completed","code":"def + fib(n):\r\n a, b = 0, 1\r\n for _ in range(n):\r\n a, b = b, + a + b\r\n return a\r\n\r\nfib_20 = fib(20)\r\nfib_20","container_id":"cntr_6a5fea2aff5c8193a1065b8c97a102160cefcd69266ffa1c","outputs":null},"output_index":0,"sequence_number":55} + + + event: response.output_item.added + + data: {"type":"response.output_item.added","item":{"id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":1,"sequence_number":56} + + + event: response.content_part.added + + data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","output_index":1,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":57} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"The","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"B31oPGxZma4BA","output_index":1,"sequence_number":58} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" ","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"Ej7MjFW0jzcysH5","output_index":1,"sequence_number":59} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"20","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"G8mLU9zXiPgkMP","output_index":1,"sequence_number":60} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"th","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"qMwCiACkYlAKmF","output_index":1,"sequence_number":61} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" Fibonacci","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"XJc5FN","output_index":1,"sequence_number":62} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" number","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"7pjY71ZXK","output_index":1,"sequence_number":63} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" (","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"VLCfOyJjxHu6D3","output_index":1,"sequence_number":64} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"0","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"zpHkvQvWBKELgkk","output_index":1,"sequence_number":65} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"-index","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"HcAhqsIBL9","output_index":1,"sequence_number":66} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"ed","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"vtE9neD9rdvAkO","output_index":1,"sequence_number":67} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":")","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"TCufQBYnYU5nyrz","output_index":1,"sequence_number":68} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"5xBB7vmssQsym","output_index":1,"sequence_number":69} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" ","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"8GAXleuAAG4kbsn","output_index":1,"sequence_number":70} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"676","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"FWjUWtduHTRUl","output_index":1,"sequence_number":71} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"5","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"JpyTnUyXeFNref8","output_index":1,"sequence_number":72} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"obfuscation":"cwkKBPhQ8XMlsfb","output_index":1,"sequence_number":73} + + + event: response.output_text.done + + data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","logprobs":[],"output_index":1,"sequence_number":74,"text":"The + 20th Fibonacci number (0-indexed) is 6765."} + + + event: response.content_part.done + + data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","output_index":1,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The + 20th Fibonacci number (0-indexed) is 6765."},"sequence_number":75} + + + event: response.output_item.done + + data: {"type":"response.output_item.done","item":{"id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The + 20th Fibonacci number (0-indexed) is 6765."}],"role":"assistant"},"output_index":1,"sequence_number":76} + + + event: response.completed + + data: {"type":"response.completed","response":{"id":"resp_050aaa8793a4eddc016a5fea29a7988194b29400d594354572","object":"response","created_at":1784670761,"status":"completed","background":false,"completed_at":1784670765,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[{"id":"ci_050aaa8793a4eddc016a5fea2b88d8819480b746e8a50a7eec","type":"code_interpreter_call","status":"completed","code":"def + fib(n):\r\n a, b = 0, 1\r\n for _ in range(n):\r\n a, b = b, + a + b\r\n return a\r\n\r\nfib_20 = fib(20)\r\nfib_20","container_id":"cntr_6a5fea2aff5c8193a1065b8c97a102160cefcd69266ffa1c","outputs":null},{"id":"msg_050aaa8793a4eddc016a5fea2d2ee88194b6955808c3796652","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The + 20th Fibonacci number (0-indexed) is 6765."}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":266,"input_tokens_details":{"cache_write_tokens":0,"cached_tokens":0},"output_tokens":72,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":338},"user":null,"metadata":{}},"sequence_number":77} + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 21 Jul 2026 21:52:43 GMT + openai-processing-ms: + - '1738' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '30000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '29999779' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + status: + code: 200 + message: OK +version: 1 diff --git a/tests/_vcr/test_provider_openai/test_openai_code_execution_persistence.yaml b/tests/_vcr/test_provider_openai/test_openai_code_execution_persistence.yaml new file mode 100644 index 00000000..99b2f16b --- /dev/null +++ b/tests/_vcr/test_provider_openai/test_openai_code_execution_persistence.yaml @@ -0,0 +1,761 @@ +interactions: +- request: + body: '{"input": [{"role": "user", "content": [{"type": "input_text", "text": + "Use code execution to define a variable `x = 42`. Don''t print anything yet."}]}], + "model": "gpt-4.1", "store": false, "stream": true, "tools": [{"type": "code_interpreter", + "container": {"type": "auto"}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '258' + content-type: + - application/json + host: + - api.openai.com + x-stainless-async: + - 'false' + x-stainless-read-timeout: + - '600' + method: POST + uri: https://api.openai.com/v1/responses + response: + body: + string: 'event: response.created + + data: {"type":"response.created","response":{"id":"resp_0d4ae9317fe2724b016a5fea2e253c8195af712eca877e120b","object":"response","created_at":1784670766,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + + event: response.in_progress + + data: {"type":"response.in_progress","response":{"id":"resp_0d4ae9317fe2724b016a5fea2e253c8195af712eca877e120b","object":"response","created_at":1784670766,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + + event: response.output_item.added + + data: {"type":"response.output_item.added","item":{"id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","type":"code_interpreter_call","status":"in_progress","code":"","container_id":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272","outputs":null},"output_index":0,"sequence_number":2} + + + event: response.code_interpreter_call.in_progress + + data: {"type":"response.code_interpreter_call.in_progress","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","output_index":0,"sequence_number":3} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"#","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"d4QWvppFEp2cy59","output_index":0,"sequence_number":4} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" Def","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"szoCNXqqycjW","output_index":0,"sequence_number":5} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"ining","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"O5PHfDS3f4e","output_index":0,"sequence_number":6} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" the","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"Ed1UhrjYDmvp","output_index":0,"sequence_number":7} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" variable","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"M8b1lSG","output_index":0,"sequence_number":8} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" as","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"ENxcdF0u0eWLl","output_index":0,"sequence_number":9} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" instructed","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"r0sxv","output_index":0,"sequence_number":10} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"\n","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"7iGFNLiXXb44dDc","output_index":0,"sequence_number":11} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"x","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"GKOcUqm68BHE4dZ","output_index":0,"sequence_number":12} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" =","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"Ie0OfBOotxrXYR","output_index":0,"sequence_number":13} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"bMcKcl8JcFJZGOs","output_index":0,"sequence_number":14} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"42","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","obfuscation":"JWQsndjFwUYGC3","output_index":0,"sequence_number":15} + + + event: response.code_interpreter_call_code.done + + data: {"type":"response.code_interpreter_call_code.done","code":"# Defining + the variable as instructed\nx = 42","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","output_index":0,"sequence_number":16} + + + event: response.code_interpreter_call.interpreting + + data: {"type":"response.code_interpreter_call.interpreting","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","output_index":0,"sequence_number":17} + + + event: response.code_interpreter_call.completed + + data: {"type":"response.code_interpreter_call.completed","item_id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","output_index":0,"sequence_number":18} + + + event: response.output_item.done + + data: {"type":"response.output_item.done","item":{"id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","type":"code_interpreter_call","status":"completed","code":"# + Defining the variable as instructed\nx = 42","container_id":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272","outputs":null},"output_index":0,"sequence_number":19} + + + event: response.output_item.added + + data: {"type":"response.output_item.added","item":{"id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":1,"sequence_number":20} + + + event: response.content_part.added + + data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","output_index":1,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":21} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"The","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"PWsdZS1zbFmZQ","output_index":1,"sequence_number":22} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" variable","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"l4bEdt8","output_index":1,"sequence_number":23} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" `","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"i14yOm4qTHwO8o","output_index":1,"sequence_number":24} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"x","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"lo0uUywkpTpR303","output_index":1,"sequence_number":25} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"`","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"lbUipnflWmXscV7","output_index":1,"sequence_number":26} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" has","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"MCiW62a79hBF","output_index":1,"sequence_number":27} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" been","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"Vhw5i3K7AdG","output_index":1,"sequence_number":28} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" defined","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"prLU1f64","output_index":1,"sequence_number":29} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" and","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"u7vU65Ui71LI","output_index":1,"sequence_number":30} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" set","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"nxO8lEEUw2rE","output_index":1,"sequence_number":31} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"snmGymlnDGs6s","output_index":1,"sequence_number":32} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" ","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"w3E1aiPMyAQm61z","output_index":1,"sequence_number":33} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"42","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"Zdame3gFBmtwGX","output_index":1,"sequence_number":34} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" as","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"UTEgrAYxuv7rQ","output_index":1,"sequence_number":35} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" requested","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"wALMLX","output_index":1,"sequence_number":36} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"SFfpsZeiVO8rtNa","output_index":1,"sequence_number":37} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" No","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"CDERnkjawdB0U","output_index":1,"sequence_number":38} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" output","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"7fMH40nW2","output_index":1,"sequence_number":39} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" has","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"wniTO0pvrCgF","output_index":1,"sequence_number":40} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" been","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"eAgowXJjC01","output_index":1,"sequence_number":41} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" printed","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"cTyG0CQY","output_index":1,"sequence_number":42} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"6X1iyhkvwoTR5xM","output_index":1,"sequence_number":43} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" If","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"CgSuqJ4LpU1PB","output_index":1,"sequence_number":44} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" you","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"L1YfwpmMnAju","output_index":1,"sequence_number":45} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" need","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"Bj8OKkyo2XY","output_index":1,"sequence_number":46} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"dz09hh0YVOrcd","output_index":1,"sequence_number":47} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" use","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"7QZAofP77I6w","output_index":1,"sequence_number":48} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" or","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"yeKx2vVQ4KQtL","output_index":1,"sequence_number":49} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" display","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"cD3oeb1R","output_index":1,"sequence_number":50} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" this","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"5zliBMwkWLH","output_index":1,"sequence_number":51} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" variable","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"1dHq2fC","output_index":1,"sequence_number":52} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"OtY2Jjy5sS9m7uR","output_index":1,"sequence_number":53} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" please","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"xrzi72B5U","output_index":1,"sequence_number":54} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" let","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"TXiGSWSQHDqA","output_index":1,"sequence_number":55} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" me","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"gRgonpTGjg8z2","output_index":1,"sequence_number":56} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" know","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"4MMjvwYzBfh","output_index":1,"sequence_number":57} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" your","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"gOcAxSoz7zR","output_index":1,"sequence_number":58} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" next","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"LFB04ZR8Pfc","output_index":1,"sequence_number":59} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" step","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"2IdtLG8pc9X","output_index":1,"sequence_number":60} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"!","item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"obfuscation":"LSeylVARYa1infh","output_index":1,"sequence_number":61} + + + event: response.output_text.done + + data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","logprobs":[],"output_index":1,"sequence_number":62,"text":"The + variable `x` has been defined and set to 42 as requested. No output has been + printed. If you need to use or display this variable, please let me know your + next step!"} + + + event: response.content_part.done + + data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","output_index":1,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The + variable `x` has been defined and set to 42 as requested. No output has been + printed. If you need to use or display this variable, please let me know your + next step!"},"sequence_number":63} + + + event: response.output_item.done + + data: {"type":"response.output_item.done","item":{"id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The + variable `x` has been defined and set to 42 as requested. No output has been + printed. If you need to use or display this variable, please let me know your + next step!"}],"role":"assistant"},"output_index":1,"sequence_number":64} + + + event: response.completed + + data: {"type":"response.completed","response":{"id":"resp_0d4ae9317fe2724b016a5fea2e253c8195af712eca877e120b","object":"response","created_at":1784670766,"status":"completed","background":false,"completed_at":1784670769,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[{"id":"ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d","type":"code_interpreter_call","status":"completed","code":"# + Defining the variable as instructed\nx = 42","container_id":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272","outputs":null},{"id":"msg_0d4ae9317fe2724b016a5fea3122508195886023a6159ea907","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The + variable `x` has been defined and set to 42 as requested. No output has been + printed. If you need to use or display this variable, please let me know your + next step!"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":207,"input_tokens_details":{"cache_write_tokens":0,"cached_tokens":0},"output_tokens":60,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":267},"user":null,"metadata":{}},"sequence_number":65} + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 21 Jul 2026 21:52:47 GMT + openai-processing-ms: + - '1426' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '30000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '29999800' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + status: + code: 200 + message: OK +- request: + body: '{"input": [{"role": "user", "content": [{"type": "input_text", "text": + "Use code execution to define a variable `x = 42`. Don''t print anything yet."}]}, + {"id": "ci_0d4ae9317fe2724b016a5fea2fcc38819591480e57c25fa43d", "code": "# Defining + the variable as instructed\nx = 42", "container_id": "cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272", + "outputs": null, "status": "completed", "type": "code_interpreter_call"}, {"role": + "assistant", "content": [{"type": "output_text", "text": "The variable `x` has + been defined and set to 42 as requested. No output has been printed. If you + need to use or display this variable, please let me know your next step!", "annotations": + []}], "status": "completed", "type": "message", "id": "msg_missing_id"}, {"role": + "user", "content": [{"type": "input_text", "text": "Now use code execution to + print `x * 2`, reusing the same `x` from before -- don''t redefine it."}]}], + "model": "gpt-4.1", "store": false, "stream": true, "tools": [{"type": "code_interpreter", + "container": "cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272"}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '1021' + content-type: + - application/json + host: + - api.openai.com + x-stainless-async: + - 'false' + x-stainless-read-timeout: + - '600' + method: POST + uri: https://api.openai.com/v1/responses + response: + body: + string: 'event: response.created + + data: {"type":"response.created","response":{"id":"resp_0d4ae9317fe2724b016a5fea31f17c8195b3036834d2f69bbc","object":"response","created_at":1784670770,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + + event: response.in_progress + + data: {"type":"response.in_progress","response":{"id":"resp_0d4ae9317fe2724b016a5fea31f17c8195b3036834d2f69bbc","object":"response","created_at":1784670770,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + + event: response.output_item.added + + data: {"type":"response.output_item.added","item":{"id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","type":"code_interpreter_call","status":"in_progress","code":"","container_id":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272","outputs":null},"output_index":0,"sequence_number":2} + + + event: response.code_interpreter_call.in_progress + + data: {"type":"response.code_interpreter_call.in_progress","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","output_index":0,"sequence_number":3} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"#","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"WrNZgsF7HRKYuKp","output_index":0,"sequence_number":4} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" Printing","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"vZ7N3oW","output_index":0,"sequence_number":5} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" x","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"SleZWvqYxtsgNZ","output_index":0,"sequence_number":6} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" *","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"6NCUrDSMBDUK2X","output_index":0,"sequence_number":7} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"hSmTi7th6CKoQ7e","output_index":0,"sequence_number":8} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"2","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"DRjlQl5MCuUWzvl","output_index":0,"sequence_number":9} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" using","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"lXwDD7QiMf","output_index":0,"sequence_number":10} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" the","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"EvLiwZQbJpdO","output_index":0,"sequence_number":11} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" previously","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"HHl8A","output_index":0,"sequence_number":12} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" defined","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"D2EWgMOS","output_index":0,"sequence_number":13} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" x","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"X6hMH4ZpdeI6F4","output_index":0,"sequence_number":14} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"\n","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"LLq8WfqTe7czPsr","output_index":0,"sequence_number":15} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"print","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"TXFh905h5nM","output_index":0,"sequence_number":16} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"(x","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"1XYU3w11DO14Rz","output_index":0,"sequence_number":17} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" *","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"XHGll7wW883wG5","output_index":0,"sequence_number":18} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":" ","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"DfDk3HJDkiKtpA5","output_index":0,"sequence_number":19} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":"2","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"yUWLH1xSSMWmViR","output_index":0,"sequence_number":20} + + + event: response.code_interpreter_call_code.delta + + data: {"type":"response.code_interpreter_call_code.delta","delta":")","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","obfuscation":"ooxuFb1mGvWMJ1A","output_index":0,"sequence_number":21} + + + event: response.code_interpreter_call_code.done + + data: {"type":"response.code_interpreter_call_code.done","code":"# Printing + x * 2 using the previously defined x\nprint(x * 2)","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","output_index":0,"sequence_number":22} + + + event: response.code_interpreter_call.interpreting + + data: {"type":"response.code_interpreter_call.interpreting","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","output_index":0,"sequence_number":23} + + + event: response.code_interpreter_call.completed + + data: {"type":"response.code_interpreter_call.completed","item_id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","output_index":0,"sequence_number":24} + + + event: response.output_item.done + + data: {"type":"response.output_item.done","item":{"id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","type":"code_interpreter_call","status":"completed","code":"# + Printing x * 2 using the previously defined x\nprint(x * 2)","container_id":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272","outputs":null},"output_index":0,"sequence_number":25} + + + event: response.output_item.added + + data: {"type":"response.output_item.added","item":{"id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":1,"sequence_number":26} + + + event: response.content_part.added + + data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","output_index":1,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":27} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"The","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"9UIUUv0NHUJhT","output_index":1,"sequence_number":28} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" result","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"OGQTvIbod","output_index":1,"sequence_number":29} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"3cYQuXS0uLYTl","output_index":1,"sequence_number":30} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" `","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"2iG9cKfdkN1n44","output_index":1,"sequence_number":31} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"x","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"870aIDVrQlsKInh","output_index":1,"sequence_number":32} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" *","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"Gh0PjrKYBoAyaA","output_index":1,"sequence_number":33} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" ","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"P6w1iw8BnWPrvgV","output_index":1,"sequence_number":34} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"2","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"KbjqtsBvdxx0cJK","output_index":1,"sequence_number":35} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"`","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"ueV8kCL02hka8Qv","output_index":1,"sequence_number":36} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" using","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"z3t0mnfJ2M","output_index":1,"sequence_number":37} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"Ajr5ZbAdNbGD","output_index":1,"sequence_number":38} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" previously","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"dJOaz","output_index":1,"sequence_number":39} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" defined","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"hu84Jy3U","output_index":1,"sequence_number":40} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" `","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"l2ClVvh4dGO2Fv","output_index":1,"sequence_number":41} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"x","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"puuGjtqaYkdcTD9","output_index":1,"sequence_number":42} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"`","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"J6h6H0li4TjK3oK","output_index":1,"sequence_number":43} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"h1fp1AOm7Dp9I","output_index":1,"sequence_number":44} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":" ","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"9KGUXopLXojZHMc","output_index":1,"sequence_number":45} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":"84","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"wKQuk9twJDSJ2k","output_index":1,"sequence_number":46} + + + event: response.output_text.delta + + data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"obfuscation":"Q8fhCgABVCYeOOm","output_index":1,"sequence_number":47} + + + event: response.output_text.done + + data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","logprobs":[],"output_index":1,"sequence_number":48,"text":"The + result of `x * 2` using the previously defined `x` is 84."} + + + event: response.content_part.done + + data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","output_index":1,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The + result of `x * 2` using the previously defined `x` is 84."},"sequence_number":49} + + + event: response.output_item.done + + data: {"type":"response.output_item.done","item":{"id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The + result of `x * 2` using the previously defined `x` is 84."}],"role":"assistant"},"output_index":1,"sequence_number":50} + + + event: response.completed + + data: {"type":"response.completed","response":{"id":"resp_0d4ae9317fe2724b016a5fea31f17c8195b3036834d2f69bbc","object":"response","created_at":1784670770,"status":"completed","background":false,"completed_at":1784670773,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","moderation":null,"output":[{"id":"ci_0d4ae9317fe2724b016a5fea33959881958ac3a10224a0921d","type":"code_interpreter_call","status":"completed","code":"# + Printing x * 2 using the previously defined x\nprint(x * 2)","container_id":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272","outputs":null},{"id":"msg_0d4ae9317fe2724b016a5fea35163c8195b20b1772770f8d60","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The + result of `x * 2` using the previously defined `x` is 84."}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"code_interpreter","container":"cntr_6a5fea2f61108193b410afd5e492da560f7799a7edfbd272"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":313,"input_tokens_details":{"cache_write_tokens":0,"cached_tokens":0},"output_tokens":46,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":359},"user":null,"metadata":{}},"sequence_number":51} + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 21 Jul 2026 21:52:51 GMT + openai-processing-ms: + - '1363' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '30000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '29999702' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + status: + code: 200 + message: OK +version: 1 From 1bb43f8776ce45c4e3d0229c73702fe094327341 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 21 Jul 2026 17:56:22 -0500 Subject: [PATCH 9/9] fix: capture Anthropic container id from streaming message_delta 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. --- chatlas/_provider_anthropic.py | 4 +++ tests/test_provider_anthropic.py | 52 ++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/chatlas/_provider_anthropic.py b/chatlas/_provider_anthropic.py index c055c68b..3fdf9c43 100644 --- a/chatlas/_provider_anthropic.py +++ b/chatlas/_provider_anthropic.py @@ -605,6 +605,10 @@ def stream_merge_chunks(self, completion, chunk): completion.stop_reason = chunk.delta.stop_reason completion.stop_sequence = chunk.delta.stop_sequence completion.usage.output_tokens = chunk.usage.output_tokens + # Anthropic only sends `container` (e.g. from the code execution + # tool) on the message_delta event, not message_start. + if chunk.delta.container is not None: + completion.container = chunk.delta.container return completion diff --git a/tests/test_provider_anthropic.py b/tests/test_provider_anthropic.py index ec5004d6..a54a4ae8 100644 --- a/tests/test_provider_anthropic.py +++ b/tests/test_provider_anthropic.py @@ -168,9 +168,7 @@ def test_anthropic_web_search_citations(): def test_anthropic_code_execution(): def chat_fun(**kwargs): return ChatAnthropic( - kwargs={ - "default_headers": {"anthropic-beta": "code-execution-2025-05-22"} - }, + kwargs={"default_headers": {"anthropic-beta": "code-execution-2025-05-22"}}, **kwargs, ) @@ -528,3 +526,51 @@ def test_anthropic_code_execution_container_reuse(): kwargs=None, ) assert kwargs["container"] == "cntr_xyz" + + +def test_anthropic_container_captured_from_streaming_message_delta(): + """Anthropic only sends `container` on the message_delta event (not + message_start), so the streaming accumulator must copy it over -- otherwise + cross-turn container reuse silently never works when streaming (the + default `chat.chat()` mode).""" + from anthropic.types import Message, RawMessageDeltaEvent, RawMessageStartEvent + from chatlas._provider_anthropic import AnthropicProvider + + chat = ChatAnthropic() + provider = chat.provider + assert isinstance(provider, AnthropicProvider) + + start_message = Message.model_validate( + { + "id": "msg_1", + "type": "message", + "role": "assistant", + "model": "claude-opus-4-6", + "content": [], + "stop_reason": None, + "usage": {"input_tokens": 10, "output_tokens": 0}, + } + ) + start_chunk = RawMessageStartEvent.model_validate( + {"type": "message_start", "message": start_message.model_dump()} + ) + completion = provider.stream_merge_chunks(None, start_chunk) + assert completion.container is None + + delta_chunk = RawMessageDeltaEvent.model_validate( + { + "type": "message_delta", + "delta": { + "stop_reason": "end_turn", + "stop_sequence": None, + "container": { + "id": "cntr_stream_xyz", + "expires_at": "2026-07-21T00:00:00Z", + }, + }, + "usage": {"output_tokens": 5}, + } + ) + completion = provider.stream_merge_chunks(completion, delta_chunk) + assert completion.container is not None + assert completion.container.id == "cntr_stream_xyz"