diff --git a/.inline-snapshot/external/cd8d3d185e7a993935ab650e0e5c6a7970758bac7d0487f9b1afaad2cc095f3c.json b/.inline-snapshot/external/cd8d3d185e7a993935ab650e0e5c6a7970758bac7d0487f9b1afaad2cc095f3c.json
deleted file mode 100644
index 1de1d9f3..00000000
--- a/.inline-snapshot/external/cd8d3d185e7a993935ab650e0e5c6a7970758bac7d0487f9b1afaad2cc095f3c.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01C63EzoQa1oa1eNn778a6ep\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":3,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I'll get\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the weather for San Francisco for you.\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_011V184uLZKFsxYfCJSjiQ6q\",\"name\":\"get_weather\",\"input\":{}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"loc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"ati\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"on\\\": \\\"San Fr\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"anc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"isc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"o, CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"units\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\": \\\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":85} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01Vm8Ddgc8qm4iuUSKbf6jku\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":781,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":6,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The weather in San Francisco,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" CA is currently **Sunny** with a temperature of **\"}}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"68°F**.\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":781,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":25} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
-]
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1fd93e01..b401785e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -99,7 +99,6 @@ $ ./scripts/test
### Snapshots
Some tests use [inline-snapshot](https://15r10nk.github.io/inline-snapshot/latest/). To update them after making changes, rerun the tests with the `--inline-snapshot=fix` and `-n0` options:
-
```bash
./scripts/test --inline-snapshot=fix -n0
```
@@ -108,15 +107,14 @@ Some tests use [inline-snapshot](https://15r10nk.github.io/inline-snapshot/lates
> `inline-snapshot` is incompatible with [pytest-xdist](https://github.com/pytest-dev/pytest-xdist), so you need to disable parallel execution `(-n0)` when using the `--inline-snapshot` option.
In addition, some tests capture snapshots of the HTTP requests they make.
-To refresh these snapshots, run the tests with the `ANTHROPIC_LIVE=1` environment variable enabled.
-
+To refresh these snapshots, run the tests with the `--http-record` flag:
```bash
-ANTHROPIC_LIVE=1 ./scripts/test --inline-snapshot=fix
+./scripts/test --inline-snapshot=fix --http-record -n0
```
> [!NOTE]
-> Sometimes it makes sense to update only the inline snapshots `(--inline-snapshot=fix)` without refreshing the HTTP snapshots `(ANTHROPIC_LIVE=1)`.
-> This is useful when the endpoint hasn’t changed, but your code handles the response differently and the assertions need updating.
+> Sometimes it makes sense to update only the inline snapshots `(--inline-snapshot=fix)` without refreshing the HTTP snapshots `(--http-record)`.
+> This is useful when the endpoint hasn't changed, but your code handles the response differently and the assertions need updating.
## Linting and formatting
diff --git a/pyproject.toml b/pyproject.toml
index 31186601..39a3971b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -74,6 +74,7 @@ dev = [
"pytest-xdist>=3.6.1",
"inline-snapshot>=0.28.0",
"griffe>=1",
+ "http-snapshot[httpx]==0.1.6",
]
pydantic-v1 = [
"pydantic>=1.9.0,<2",
diff --git a/tests/conftest.py b/tests/conftest.py
index b1ac2570..0a9152d1 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,6 +8,7 @@
import httpx
import pytest
+from http_snapshot import SnapshotSerializerOptions
from pytest_asyncio import is_async_test
from anthropic import Anthropic, AsyncAnthropic, DefaultAioHttpClient
@@ -20,6 +21,18 @@
logging.getLogger("anthropic").setLevel(logging.DEBUG)
+SNAPSHOT_RESPONSE_HEADERS_EXCLUDE = [
+ "date",
+ "request-id",
+ "anthropic-organization-id",
+ "x-envoy-upstream-service-time",
+ "cf-ray",
+]
+
+SNAPSHOT_REQUEST_HEADERS_EXCLUDE = [
+ "x-api-key",
+]
+
# automatically add `pytest.mark.asyncio()` to all of our async tests
# so we don't have to add that boilerplate everywhere
@@ -58,6 +71,34 @@ def client(request: FixtureRequest) -> Iterator[Anthropic]:
yield client
+@pytest.fixture
+def http_snapshot_serializer_options() -> SnapshotSerializerOptions:
+ return SnapshotSerializerOptions(
+ exclude_response_headers=SNAPSHOT_RESPONSE_HEADERS_EXCLUDE,
+ exclude_request_headers=SNAPSHOT_REQUEST_HEADERS_EXCLUDE,
+ include_request=True,
+ )
+
+
+@pytest.fixture(scope="function")
+def snapshot_client(
+ snapshot_sync_httpx_client: httpx.Client,
+ is_recording: bool,
+) -> Iterator[Anthropic]:
+ with Anthropic(http_client=snapshot_sync_httpx_client, api_key=None if is_recording else api_key) as client:
+ yield client
+
+
+@pytest.fixture(scope="function")
+async def async_snapshot_client(
+ snapshot_async_httpx_client: httpx.AsyncClient, is_recording: bool
+) -> AsyncIterator[AsyncAnthropic]:
+ async with AsyncAnthropic(
+ http_client=snapshot_async_httpx_client, api_key=None if is_recording else api_key
+ ) as client:
+ yield client
+
+
@pytest.fixture(scope="session")
async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncAnthropic]:
param = getattr(request, "param", True)
diff --git a/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages.test_stream_with_raw_schema/48aac7c3-f271-47b3-854b-af4ed31e10bb.json b/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages.test_stream_with_raw_schema/48aac7c3-f271-47b3-854b-af4ed31e10bb.json
deleted file mode 100644
index bb41bc2b..00000000
--- a/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages.test_stream_with_raw_schema/48aac7c3-f271-47b3-854b-af4ed31e10bb.json
+++ /dev/null
@@ -1,3 +0,0 @@
-[
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_01EZum6x6zixptqit3SdTzgt\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"12\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"345,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"67890]\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":10}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
-]
\ No newline at end of file
diff --git a/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages/606342ef-f614-4bc1-b5e7-2c3305a48bf3.json b/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages/606342ef-f614-4bc1-b5e7-2c3305a48bf3.json
new file mode 100644
index 00000000..c1f977e2
--- /dev/null
+++ b/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages/606342ef-f614-4bc1-b5e7-2c3305a48bf3.json
@@ -0,0 +1,65 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "NOT_GIVEN",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "AsyncAnthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "async:asyncio",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper-method": "stream",
+ "x-stainless-stream-helper": "beta.messages",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "265"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890"
+ }
+ ],
+ "model": "claude-sonnet-4-5",
+ "output_format": {
+ "type": "json_schema",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ },
+ "stream": true
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "text/event-stream; charset=utf-8",
+ "connection": "keep-alive",
+ "server": "cloudflare",
+ "cf-cache-status": "DYNAMIC",
+ "cache-control": "no-cache",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none"
+ },
+ "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_01JbzU4eQy8GBAU9N2KuLT93\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"12\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"345,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"67890]\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":10} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/_parse/test_beta_messages.py b/tests/lib/_parse/test_beta_messages.py
index b869f018..aa24cab4 100644
--- a/tests/lib/_parse/test_beta_messages.py
+++ b/tests/lib/_parse/test_beta_messages.py
@@ -1,16 +1,21 @@
+from typing import Any, cast
+
import pytest
-from respx import MockRouter
from inline_snapshot import external, snapshot
from anthropic import AsyncAnthropic, _compat
from anthropic.types.beta.parsed_beta_message import ParsedBetaMessage
-from ..snapshots import make_async_stream_snapshot_request
-
@pytest.mark.skipif(_compat.PYDANTIC_V1, reason="tool runner not supported with pydantic v1")
class TestAsyncMessages:
- async def test_stream_with_raw_schema(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [
+ cast(Any, external("uuid:606342ef-f614-4bc1-b5e7-2c3305a48bf3.json")),
+ ],
+ )
+ async def test_stream_with_raw_schema(self, async_snapshot_client: AsyncAnthropic) -> None:
async def async_stream_parse(client: AsyncAnthropic) -> ParsedBetaMessage[None]:
async with client.beta.messages.stream(
model="claude-sonnet-4-5",
@@ -32,12 +37,6 @@ async def async_stream_parse(client: AsyncAnthropic) -> ParsedBetaMessage[None]:
) as stream:
return await stream.get_final_message()
- response = await make_async_stream_snapshot_request(
- async_stream_parse,
- content_snapshot=external("uuid:48aac7c3-f271-47b3-854b-af4ed31e10bb.json"),
- respx_mock=respx_mock,
- mock_client=async_client,
- path="/v1/messages?beta=true",
- )
+ response = await async_stream_parse(async_snapshot_client)
- assert response.content[0].text == snapshot("[12345,67890]")
+ assert response.content[0].text == snapshot("[12345,67890]") # type: ignore
diff --git a/tests/lib/snapshots.py b/tests/lib/snapshots.py
deleted file mode 100644
index 5ca46365..00000000
--- a/tests/lib/snapshots.py
+++ /dev/null
@@ -1,259 +0,0 @@
-from __future__ import annotations
-
-import os
-import json
-from typing import Any, List, Callable, Awaitable, cast
-from typing_extensions import TypeVar
-
-import httpx
-from respx import MockRouter
-from inline_snapshot import outsource, get_snapshot_value
-
-from anthropic import Anthropic, AsyncAnthropic
-from anthropic._utils._utils import is_list
-
-_T = TypeVar("_T")
-
-
-def make_snapshot_request(
- func: Callable[[Anthropic], _T],
- *,
- content_snapshot: Any,
- respx_mock: MockRouter,
- mock_client: Anthropic,
- path: str,
-) -> _T:
- live = os.environ.get("ANTHROPIC_LIVE") == "1"
- collected: list[str] = []
- if live:
-
- def _on_response(response: httpx.Response) -> None:
- collected.append(json.dumps(json.loads(response.read())))
-
- respx_mock.stop()
-
- client = Anthropic(
- http_client=httpx.Client(
- event_hooks={
- "response": [_on_response],
- }
- )
- )
- else:
- responses = get_snapshot_value(content_snapshot)
-
- if not is_list(responses):
- responses = [responses]
-
- curr = 0
-
- def get_response(_request: httpx.Request) -> httpx.Response:
- nonlocal curr
- content = responses[curr]
- assert isinstance(content, str)
-
- curr += 1
- return httpx.Response(
- 200,
- content=content,
- headers={"content-type": "application/json"},
- )
-
- respx_mock.post(path).mock(side_effect=get_response)
-
- client = mock_client
-
- result = func(client)
-
- if not live:
- return result
-
- client.close()
-
- if len(collected) == 1:
- assert collected[0] == content_snapshot
- else:
- assert collected == content_snapshot
-
- return result
-
-
-def make_stream_snapshot_request(
- func: Callable[[Anthropic], _T],
- *,
- content_snapshot: Any,
- respx_mock: MockRouter,
- mock_client: Anthropic,
- path: str,
-) -> _T:
- live = os.environ.get("ANTHROPIC_LIVE") == "1"
- collected: list[str] = []
- if live:
-
- def _on_response(response: httpx.Response) -> None:
- response.read()
- collected.append(response.text)
-
- respx_mock.stop()
-
- client = Anthropic(
- http_client=httpx.Client(
- event_hooks={
- "response": [_on_response],
- }
- )
- )
- else:
- response_contents = get_snapshot_value(content_snapshot)
- assert is_list(response_contents)
-
- response_contents = cast(
- List[str],
- response_contents,
- )
-
- curr = 0
-
- def get_response(_request: httpx.Request) -> httpx.Response:
- nonlocal curr
- content = response_contents[curr]
- assert isinstance(content, str)
-
- curr += 1
- return httpx.Response(
- 200,
- content=content.encode("utf-8"),
- headers={"content-type": "text/event-stream"},
- )
-
- respx_mock.post(path).mock(side_effect=get_response)
- client = mock_client
-
- result = func(client)
- if not live:
- return result
-
- assert outsource(collected) == content_snapshot
- return result
-
-
-async def make_async_stream_snapshot_request(
- func: Callable[[AsyncAnthropic], Awaitable[_T]],
- *,
- content_snapshot: Any,
- respx_mock: MockRouter,
- mock_client: AsyncAnthropic,
- path: str,
-) -> _T:
- live = os.environ.get("ANTHROPIC_LIVE") == "1"
- collected: list[str] = []
- if live:
-
- async def _on_response(response: httpx.Response) -> None:
- await response.aread()
- collected.append(response.text)
-
- respx_mock.stop()
-
- client = AsyncAnthropic(
- http_client=httpx.AsyncClient(
- event_hooks={
- "response": [_on_response],
- }
- )
- )
- else:
- response_contents = get_snapshot_value(content_snapshot)
- assert is_list(response_contents)
-
- response_contents = cast(
- List[str],
- response_contents,
- )
-
- curr = 0
-
- def get_response(_request: httpx.Request) -> httpx.Response:
- nonlocal curr
- content = response_contents[curr]
- assert isinstance(content, str)
-
- curr += 1
- return httpx.Response(
- 200,
- content=content.encode("utf-8"),
- headers={"content-type": "text/event-stream"},
- )
-
- respx_mock.post(path).mock(side_effect=get_response)
- client = mock_client
-
- result = await func(client)
- if not live:
- return result
-
- assert outsource(collected) == content_snapshot
- return result
-
-
-async def make_async_snapshot_request(
- func: Callable[[AsyncAnthropic], Awaitable[_T]],
- *,
- content_snapshot: Any,
- respx_mock: MockRouter,
- mock_client: AsyncAnthropic,
- path: str,
-) -> _T:
- live = os.environ.get("ANTHROPIC_LIVE") == "1"
- collected: list[str] = []
- if live:
-
- async def _on_response(response: httpx.Response) -> None:
- collected.append(json.dumps(json.loads(await response.aread())))
-
- respx_mock.stop()
-
- client = AsyncAnthropic(
- http_client=httpx.AsyncClient(
- event_hooks={
- "response": [_on_response],
- }
- )
- )
- else:
- responses = get_snapshot_value(content_snapshot)
-
- if not is_list(responses):
- responses = [responses]
-
- curr = 0
-
- def get_response(_request: httpx.Request) -> httpx.Response:
- nonlocal curr
- content = responses[curr]
- assert isinstance(content, str)
-
- curr += 1
- return httpx.Response(
- 200,
- content=content,
- headers={"content-type": "application/json"},
- )
-
- respx_mock.post(path).mock(side_effect=get_response)
-
- client = mock_client
-
- result = await func(client)
-
- if not live:
- return result
-
- await client.close()
-
- if len(collected) == 1:
- assert collected[0] == content_snapshot
- else:
- assert collected == content_snapshot
-
- return result
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_compaction_control/ab7b2edd-9c2d-4f53-9c04-92bb659b9caa.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_compaction_control/ab7b2edd-9c2d-4f53-9c04-92bb659b9caa.json
deleted file mode 100644
index aee71e4d..00000000
--- a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_compaction_control/ab7b2edd-9c2d-4f53-9c04-92bb659b9caa.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "{\"model\": \"claude-sonnet-4-5-20250929\", \"id\": \"msg_0191gBaqZk5tPXrUvt9e6iaU\", \"type\": \"message\", \"role\": \"assistant\", \"content\": [{\"type\": \"text\", \"text\": \"I'll write a detailed 500-word essay about dogs, cats, and birds for you.\\n\\n---\\n\\n**Our Beloved Companions: Dogs, Cats, and Birds**\\n\\nThroughout human history, animals have played an integral role in our lives, providing companionship, entertainment, and emotional support. Among the most popular pets worldwide are dogs, cats, and birds, each offering unique characteristics and forming special bonds with their human caretakers.\\n\\n**Dogs: Loyal Friends and Protectors**\\n\\nDogs have earned their reputation as \\\"man's best friend\\\" through thousands of years of domestication and companionship. These remarkable animals are known for their unwavering loyalty, intelligence, and adaptability. From tiny Chihuahuas to massive Great Danes, dogs come in an astounding variety of breeds, each developed for specific purposes ranging from herding and hunting to companionship and service work.\\n\\nWhat makes dogs particularly special is their ability to form deep emotional connections with humans. They can read human emotions, respond to training, and provide comfort during difficult times. Dogs require regular exercise, social interaction, and mental stimulation to thrive. Their playful nature and enthusiasm for life can brighten even the darkest days. Many dogs serve in specialized roles as service animals, therapy dogs, search and rescue workers, and police partners, demonstrating their versatility and intelligence.\\n\\n**Cats: Independent and Enigmatic**\\n\\nCats offer a different kind of companionship, characterized by independence and mysterious charm. These graceful felines have been associated with humans for thousands of years, originally valued for their prowess in controlling rodent populations. Today, cats are beloved for their low-maintenance care requirements and affectionate, albeit selective, nature.\\n\\nUnlike dogs, cats are generally more self-sufficient and require less direct attention, making them ideal for busy households or individuals with limited space. They are naturally clean animals, spending hours grooming themselves and instinctively using litter boxes. Cats communicate through various vocalizations, body language, and purring\\u2014a soothing sound that has been shown to have therapeutic effects on human health. Their playful hunting instincts emerge during play sessions, and their quiet companionship provides comfort without the demanding neediness sometimes associated with other pets.\\n\\n**Birds: Colorful and Vocal Companions**\\n\\nBirds bring vibrant colors, melodious songs, and surprising intelligence into homes around the world. From small budgerigars to large parrots, pet birds offer unique interactive experiences. Many bird species are highly intelligent, capable of learning tricks, mimicking speech, and solving puzzles. Their cognitive abilities often surprise those unfamiliar with avian pets.\\n\\nBirds are social creatures that thrive on interaction, both with their human caretakers and, ideally, with other birds. They require specialized care, including proper nutrition, spacious cages, toys for mental stimulation, and regular veterinary checkups. Their beautiful plumage and cheerful songs can transform a home's atmosphere, providing natural entertainment and beauty. Some species, particularly parrots, can live for decades, forming lifelong bonds with their owners.\\n\\n**Conclusion**\\n\\nDogs, cats, and birds each offer distinct advantages as companions, and the choice between them often depends on lifestyle, living situation, and personal preferences. Dogs provide active companionship and loyalty, cats offer independent affection and ease of care, and birds bring beauty and intelligence in compact packages. Regardless of which animal someone chooses, the bond between humans and their pets enriches lives immeasurably, teaching us about responsibility, empathy, and unconditional love.\\n\\n---\"}, {\"type\": \"tool_use\", \"id\": \"toolu_0147W1ET6XbT4dqWtoCugLgd\", \"name\": \"submit_analysis\", \"input\": {\"summary\": \"This comprehensive 500-word essay explores three popular companion animals: dogs, cats, and birds. Dogs are presented as loyal, intelligent, and versatile companions known for their emotional bonds with humans and their roles in service work. They require regular exercise and social interaction, coming in diverse breeds suited for various purposes. Cats are described as independent, low-maintenance pets that offer selective affection and therapeutic benefits through their purring and calm presence. They are naturally clean and ideal for busy households. Birds are highlighted for their vibrant colors, intelligence, and ability to mimic speech and learn tricks. They require specialized care and social interaction, with some species living for decades. The essay concludes that each animal offers unique benefits suited to different lifestyles, and all provide valuable companionship that enriches human lives through responsibility, empathy, and unconditional love.\"}}], \"stop_reason\": \"tool_use\", \"stop_sequence\": null, \"usage\": {\"input_tokens\": 617, \"cache_creation_input_tokens\": 0, \"cache_read_input_tokens\": 0, \"cache_creation\": {\"ephemeral_5m_input_tokens\": 0, \"ephemeral_1h_input_tokens\": 0}, \"output_tokens\": 998, \"service_tier\": \"standard\"}}",
- "{\"model\": \"claude-sonnet-4-5-20250929\", \"id\": \"msg_01KkGypgLRVfvx2SP81PMKYy\", \"type\": \"message\", \"role\": \"assistant\", \"content\": [{\"type\": \"text\", \"text\": \"\\n## 1. Task Overview\\nThe user requests a 500-word essay about dogs, cats, and birds, followed by a single call to the `submit_analysis` tool at the end containing information about all three animals. \\n\\n**Key constraints:**\\n- Essay must be detailed and approximately 500 words\\n- Must cover all three animals: dogs, cats, and birds\\n- Tool `submit_analysis` must be called exactly once, at the end\\n- Tool call should contain information about all three animals\\n\\n## 2. Current State\\n**Completed:** Nothing has been completed yet.\\n\\n**Status:** The task has been acknowledged but no essay has been written and no tool has been called.\\n\\n**Artifacts produced:** None yet.\\n\\n## 3. Important Discoveries\\n**Technical requirements:**\\n- Need to understand the parameters/schema for `submit_analysis` tool (not yet verified)\\n- Must structure the tool call to include data about all three animal types in a single invocation\\n\\n**Approach to take:**\\n- Write a comprehensive 500-word essay discussing dogs, cats, and birds\\n- Essay should cover characteristics, behaviors, and comparisons between the three\\n- Extract/organize key information about each animal for the tool call\\n- Call `submit_analysis` once with consolidated data about all three animals\\n\\n## 4. Next Steps\\n1. **Write the 500-word essay** covering:\\n - Dogs: characteristics, behavior, relationship with humans\\n - Cats: characteristics, behavior, relationship with humans\\n - Birds: characteristics, behavior, diversity\\n - Comparisons and contrasts between the three\\n \\n2. **Determine the schema for `submit_analysis` tool** - check what parameters it accepts and how to structure data about multiple animals\\n\\n3. **Call `submit_analysis` once** with information about all three animals in the appropriate format\\n\\n4. **Verify word count** is approximately 500 words\\n\\n## 5. Context to Preserve\\n- User emphasized calling the tool \\\"only once at the end\\\"\\n- Essay should be \\\"detailed\\\" - not superficial\\n- The tool call must encompass information about all three animals, not separate calls per animal\\n- This appears to be a test of following multi-step instructions precisely\\n\"}], \"stop_reason\": \"end_turn\", \"stop_sequence\": null, \"usage\": {\"input_tokens\": 324, \"cache_creation_input_tokens\": 0, \"cache_read_input_tokens\": 0, \"cache_creation\": {\"ephemeral_5m_input_tokens\": 0, \"ephemeral_1h_input_tokens\": 0}, \"output_tokens\": 496, \"service_tier\": \"standard\"}}"
-]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_server_side_tool/a0a711eb-ee0e-4a42-88d6-5c7f83c0f25a.txt b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_server_side_tool/a0a711eb-ee0e-4a42-88d6-5c7f83c0f25a.txt
deleted file mode 100644
index ee9bf495..00000000
--- a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_server_side_tool/a0a711eb-ee0e-4a42-88d6-5c7f83c0f25a.txt
+++ /dev/null
@@ -1 +0,0 @@
-{"model": "claude-haiku-4-5-20251001", "id": "msg_01NPxpnUAguLtyGiU3MzGnJD", "type": "message", "role": "assistant", "content": [{"type": "server_tool_use", "id": "srvtoolu_01RmcvokSHdbn2mkwmyHKURE", "name": "web_search", "input": {"query": "weather SF San Francisco today"}}, {"type": "web_search_tool_result", "tool_use_id": "srvtoolu_01RmcvokSHdbn2mkwmyHKURE", "content": [{"type": "web_search_result", "title": "San Francisco, CA Current Weather | AccuWeather", "url": "https://www.accuweather.com/en/us/san-francisco/94103/current-weather/347629", "encrypted_content": "EoMRCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDPNC+JrQDAdh8YKKxhoMkoPEIJZtMw1H5iuTIjD3GVOJCrvOmXaLdfbrwAw2RLI+/kzYuhtbEzlTH18rRhJ4IVU8sYjk3XCrYT+Kp3gqhhCQGkPSD6JzhWeWprG5x1y6AwVQqdq/NCDjD4UxBnOaGZcsTNEK1vIJK30BMEi/TwbJJOMcM5FGK8zoTSxibAE+38JKWv/ukfLRhBLEehjF1aJ6w/S8F3R+grbFQDzGJRELsl9+MTyBrXgOGxy3KK7uZEJgXPgQTY1qQq2n8pmERF1BCVMn8xyH+SMfFWPe3bnryU3nzi1fUYfNzTNHLGuY8WVYJ3cdvBkkEij4Ke683m0rzulUVxXPkYgaXki1OnKGEOsx/9rBsemDv7xy7NdsA5gaHZITSasIBFRlXg3WcP/m/KWCJfB32Nc9FdOz1/BOe4lPZ6reWKd1g02Ql96S/YO/jbwpthEB0b5sQcl2eg+K8/JPa5/M7oYaoMZIt8swokW74XT3qfYvziAb9P1nWf1lFF4g+h8TzlGbw2rQ909v1H2EnoqoeGR3wZJZ2zH09UgFWbWc4OK6yG5gA+4joYwp2EXnJDNEcovnpyRf2vyUhUamKcq5cam2D47SlGozHcOlHYZuCZmUIhzb99uRrKyibcRyZ9RHtkfxr3rBJEgRcUmmoNjxk38IcLuxxGBhBZrZ1t83G3J6yow0IUrrxM52sh0dS1mom+x240NDV4LHuD9ahN2cbqdMWWbKFGzqmRr2iOVU5qvQaN+MjGJl3qbh2NyGmahtGdrE5JpTO247k7icEIJAnlJcDUgsGh3/8FmK7g9beeyyrMSQU7jUyOGU9BfzPmkY5lC+krti8vD9ddEHcuorQJOOkYZVmNZCz4ZanW1lJTG8UZOTCFG3WJO1x4DgzN9zWc0ZkkyNHX0yg/QUW6yYY8GHlYiI0zVcAAvk0Eih94P/l4dwleXkJvpUu/N/UY/8yUW/kTqcp2wnyhUmE4sQD1p62+bwl9kCDK7EROHKA1DzXVXEgFUqWs+vgmgq68gW+eULyzADLC1rWGSm/pjdal8XZ8QajPpIgwP2gpBkVLGqPbiYRUeuNQBb/OJeAlIz0FOp9WVmxaNuwv7oXKQuuvLIlod41VtwyQ6zOvKsud1sKQGNp4CPc+NKEN0gab+fKNJQLpfw7MNAYmUA9Qubf8ys2b2YPDEnNUggb7kaNuzwzfMJo9ig/gZDeZKiMv54zUsItNwaKgWAIKu1fCZCkL2wvIxGoxqp7i5QxV+jlEodSMb4p7jS2+cwgfrTSgXmf4gCdPyBT6ckdk7k6uyqOCASqiiyb1p5pdngaZDcRVAfci165DMJZKeXEfj/GCHg0HRpIXY/v0oSkfeDlMtP6gtiplmMKr/cQn8HaSPGQmIYopsChqBWNKcCKpocOIYvBJ5Xn1AXZMZJeTZFRA0xuz+WFHS6pvDXVdIqUzkLa9yBnJ+NO+Qty+zMMTIZm0ANj0G25ybo9Ce7Yza0mUrdmU2NM5R1GbwrJt1D2+I6f8wjpdbKoaGlKtauQuOL1JpUJGx2dddXQJdd+sgwz0+/2+rE7ofqjVn7j4Z7l0fvaEk9SuGIotR0KJXySN8iSBdQGy1oHguvrL9WHPyJ2PAxHPrkeSJCdClIb1sv+yPilQBUGirIQZSfXduXhff1WqjMRzQgr5l/GTdSGC3rngZMWM7IJ2jOSB+pOkZdjhZsNkynrwpZJD3OLcUXlAfnbcR9ax2YK6XGlMVYPA59pvd//bsOv8F7DyRnuuAAmAgNz74Ok2HBntEu224xf/I4MOkgOcB4A9M3i9ZtfSXXJdbvplK7Quk3nJp9nic08IzF1gKiwsJ/TtUyO77XdNF0o/nlJZyxs5N9oK8F8hnPOmUIVhmotEhF3UTDOUUvRfSaxcSP8ow0FO9vp0tvaIYmJ3N/9Yx9ES4cXIfjviLP+AfG2T2Y6M6YaQcd/XqFcFPPxqdus09gDN+RXsMIZ7+VBk8p2Y3COOcMNHv6XkWFXStC/fvtkQkz9uGzy2DS46wdJ977i7G4v3JvLfADqeowJmn8xhiV6MjvOgmVEi4Wvur4rikEjnQxIh876JkdmqPUDJqhzrUkIvNjbND2gKSzn+CuTxgcYQqPTCRMOXHEnNo23G7/GO0p26gtKfQKC+IbWqoZ2j3Vv/sDUX0C2y77FdZIozyYHIaZJyhzJYfW99vk7OqnkckSuiyo3Kc6LCU+GCybxQDnvnySg+PfU8dn10J8OXyG0Lrvix8jILzkgAqLt/XJcihSTX3CX0DLvVbLiEHoaHWqyChtDwpN4DcLlMcftJilkfbqyNzaw1UCUlN2C0nU8cnFALKpGsGfV5CI3mk5HwLJRkIWyXvCnF7pcUV7r1s9vUnaoO48E2Trya6HzlHRfZHrFAb2rwD+inflovfwuin+E61iTxgW6MtmujmGy8M7sZJ0tg2rg5Tr7JPYOa6LhI/neZ5qNSPEKeyu+KgrcV1mHQ6GhQ6A1oHZvODIGmGqaY5OscJJg5SsbTbmM5iSE8AMuaTRv9I3JZGhNz5N0t/KldWtWXzthlHnYiiuSYhQpQos2fJz/Qhdb5HOZx3jeGv4vO0O+vDaqR7sWT/61EDzJI1b7uxvWoTOvjM7VF+EHGizf1zzgP3p8zW5i/2VhbkrLOnTRZnKn9h2OyGD7D3KIDRc9mzUchSkXRlf5PytC2Brvhnh+oW5SISNurrjVZ2g51egwqA+UEDJvLOObuceK5yo9BZMm5wcOplf6lUEi+1cxV/ET6R2QrZgvGuah2kVx9ktYdJf8r06xnFqw++oz6kTYjWg1H8h3dA5vAOxyqp5Z/08rBCCVRgD", "page_age": null}, {"type": "web_search_result", "title": "San Francisco Bay Area, CA", "url": "https://www.weather.gov/mtr/", "encrypted_content": "EtQCCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDJYwe5sfziwkyg0rlBoMsyPrfCA3WBM37U11IjBJaJ1zUVqikbCNdCe7DTLqbp8scvbE7awCz2dDVoc3W90ziM8ahDWC1d/ZYRK2Yn8q1wFdQ4OLZRe6Xogb3rIyZpC6izgCBZkadz2alIhT1I2jSROB3VlfXwqfO2MAT0tStI0X/ApUjGyafe/icIw/3AtdTviqKEQwmrZ9MVMH0syYaz4RIyn/7ORNw5FcN50JUpPNHVjNAcXbtq91MIjlqGAaLu17jYZy+K9Qj6Xm1A3panidk1/tlcjoDKNoZxPiwB48uwkEWKfaJ6ZtHPG9fbFkSvI64sItzevo9l1W87whIcSpEvWyykWxULOFoDzg2CsD+HvDomfZMpXvwTV7AQyXcLM6cuPLphgD", "page_age": null}, {"type": "web_search_result", "title": "San Francisco, CA Weather Forecast | AccuWeather", "url": "https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629", "encrypted_content": "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", "page_age": null}, {"type": "web_search_result", "title": "San Francisco, CA Weather Conditions | Weather Underground", "url": "https://www.wunderground.com/weather/us/ca/san-francisco", "encrypted_content": "Et8FCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDN3O1GnCiRxUE3IXwBoMlZbhTkEMpw7d2XnoIjCzSr5J1SLl3FjUTPQqXJ3GUurwJWssdKuBKrA8QlPv+bd8ErPF+6X/y2pd9p4P6gwq4gTSIwlvGVK63FUxZcEi3GK/iOCA0yBrJErLS9+1yoszLWoe1zkl3gAW0A2uqfuOLaU1vOUdhGqpIFtyTIx1/oG9/p9lGjZF9VrAMmMHkzRTVWCdfagDUgoGQDgEAvLI9Y4NJ0nuB9GnFhPNdpiKq3C7LtAd/+6i6jm/5Zl7b2R5kVSAXTWOzHmGhn14eBKNV++o6XkLOVF/2MkgwrrBbRMj2U07PJUElpoa1+qfRNNlKWILTAZGoJcYy7BH0eOPIUofozxYvn41LioaV4G4dwIPXOV0wTlzKOfG6MRBgj1Z4xk0fJae7mgIuuSF4pYnxD0T5tOZx8l3LsMm8685NMnDrKU98tK5Y/7uYXhDOnY78CAyHRACFS1CFLssLrb4wd8VbFTaLNSwuqz4nElo39HkHU/X7yFRv0vkMSSB6riM05P2h5jutaqCXJxT0poOJQRfS3Te3U3RaZkUjZAvzzSrh4xHHJAAJTf5vzhtysZPP7/pKyysoLbZQ1tzCKv7YrGaqHJcLO55FpVYQyVAQQ8FDCV4mRJOiyykyqXXSDid4InFaPg3d8sfZxu8alTIpuT7OGOzbB0DtlCUZ/mXaUNYzxF+IFeFLpHC7b0W4eYxIBnHLOfasLvxH5bzD0lRPlnQ0/hb1o9j7yLsKVvZtY3ezDBigOFAF/sTiIRpCLAk/BpU/oDsyN5bOHAHBzDUXQ02XwiPPE6/upDl3ATe/sU2aKDIKlxWtRZ9I2Z2eqp0VF029a06XkcVxkiMq2Bgzi0g/5JkYQUMW3mpWCc241ol2A6d4fOc3j9ckur4x7aVeee3GAM=", "page_age": null}, {"type": "web_search_result", "title": "San Francisco, CA Hourly Weather Forecast | Weather Underground", "url": "https://www.wunderground.com/hourly/us/ca/san-francisco", "encrypted_content": "EpAHCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDATwpbSmV8+sgnKr9hoMA5IWzAVP54d4q0L/IjDd6qCe5KwgF1xZKlTVFuxtv+Cjj4/wJ7XGpe+gngDcJieB8mY/lDjoYHos5WwwpQIqkwbUA24lSwTudHZWE19XGCsudsjLjCPI+tjVovlclpL3ayUz1dufuv54x+DxYbSaJpYmbKtWBek/36sriP2dgO03vPpVnayDZ/ggbr8B7hFmaUwj2NRGbJF/5OybMznE5AKeWnszWYr/5MTnY5sWqQiJpXGwJ96pv3BBD+x4egcvgimkXoRPE1RJNOlk/VIlu2qUJtq4w0HaQcNGKJdF5QLUJiWebHrBW5PNzUpydmU7nxeWdGSowv6Njdsk7uP4i5rFbjgZ4/WSoxA4ou+EMrAMvQUEIGiZf1jrTyhEH+hcZBEZsiGzswzsnTxNPkkc+mj2nhY1yDHDGeN0OYO2KpnjsZ0TGL02wY+HM6jFhLixHUFqyZf6svqL0wCW7CFCBQwaHNYRsY+FAho6HedFlDq4P4t9DyxPABoF1PSh0jRBsVueo2dY3nSAqHeN/VlxpBOG4BWif9FFxjjgvrAyaaQeRznzDcropAD2fbX6SKzvuVFtSGMQDGWCY5M6LCq0YGbygPbNyO5Uv2MgEDc57iJcfOAB6BT3MU9QrDszrwpP4HjD+N0j+UPo1wSvoYzizjyubLbKLEZcoDcLqo76pTOIkizGWvXK67yCzE288H11ltO4+nN1t46+sp6ZUXHTYDKNLjOt2hrj2V78oj1LKuWXokBUSt99ybzhhbQt6q6ocVQG2lcD3lo4JnQmH0+gqQ2fW4bcEAfsIGFFkfTxlN/Mo+1O1koFgTXFD7xubAruKivKHYQ2oFc7UVR2fyrSGX7zsM06XxcKJz9t+SDIUHb411zHZkfegBPI6RjyUf5GwvvKGSIQKA5BInCTOLiP+2kANXPE6t6Z/d1pp7pWLWB7RYEvibjrMmO7KQnfW+g9Q66hWw06rneFeDXVhNkhogcHp/IlsRwC5IcX8nCpmvO0e3qp3AKtJ9by6WP9izb3pkjj1RsO2N6CoguFVI1C6kucFi/fwP4Yo1fLjEIjCyKgoSOjsF/M4sk5CAIfyeJiuDs6sGPR1JElxrHB9mwFhJE9JXcbjqDy8vEKfligmEKzE2jEGAM=", "page_age": null}, {"type": "web_search_result", "title": "Zone Area Forecast for San Francisco County", "url": "https://forecast.weather.gov/MapClick.php?zoneid=CAZ006", "encrypted_content": "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", "page_age": null}, {"type": "web_search_result", "title": "Weather for San Francisco, California, USA", "url": "https://www.timeanddate.com/weather/usa/san-francisco", "encrypted_content": "EoIHCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDJUhfUPWe6QCEqL6xhoMQu+PGTxtAKXxWeVBIjBzTHEvlxvwu384Pc10vZXJ8SSMeO1x97ILnAPgv+/iBdi0DbFKt7WIHgqvKb6QmrwqhQaW7lDCJmyBgQaGPXZFZWQHLegTp2d4AIvzKY3r7QB3GX7gea/vBDUe0C1AitoIAO5Ac4Iz43kfw3F7ZT44VSXRMPO0OjuQEPb7fgqGwBZrNruJrooHThqqOYRFGN5ZqEIMN31M7r9qD4Kuf7+HzDABijJtbsHJp4BzJ3/ATMVwXSJPgdZIb/P/P2TXa6qj7TkiudP8052alm9yoZX9U9uAUA1MmHHXCQrF8iJ7oAWo9akuSgyAeE1BDniiTZe+BWLCTrsTV+QZo6PE+HZG2oGxo+UodFy1GRnXyTL1qFH108u8iyGqSdJh82OqqP2yHX5oDnHApYiQ2KMWj90K827q9QTzY7v+0A9LYTPWPPLZ9WHDnwdzsmfLynWzc6+eCIkfjHep0Xzr1QFoSAuoCjpEC8LVapurNAFYGOz1DRzKk2bQDW3ZZQlHpukUroetTALAwDbJ64KRxRefB+flaoTFN+XAqwt6gkMF97o+pcD7htqzU/PnjZUIXs32VlDQVv6AUG2j4mMDRiXSLuvSEqN0y17ySG2xjC3TBKxfQA82mE3FFY0edGIf+vzjGLSFrpxQ3e/u4WkeB7gsCcFq4cziReI4QjEjsniDmsQIXxHT5jJBVAkFUDQFHXYcbz+VrZeMu80t8qRxIpQbb0h4HlRGGNiv5sLwRdljhchQZaYH7lSNpw+V4ngfVrPJxFXOdXL7O1dVGtzbBxD6air20pgPMQy028C2w8AQXd0jyCFDW1+1r44/zvKsdbmbCFDv8bpqKC0XkbZ7M3/an9uQgnZLVbojr15V5jHPjQqggGJoRoNzJMWdfsA/dqBII7Dh2a4v2AM1Hvu5jOxoJbF9xZJfvUtfEhmjoJdtFJotJUDta8q8OvdQ9fMXaUVnC0e/FmzjoahnpYq74CDDPMEMZkT13yLBqF3yNlgE9uWqKpXAO1BuwzEn9t00CO6BYKufV8vqGa5NLNYVD+baD+8FuT1UIprk1si3O85WoPENJKXtYuggTMBJcLEE/3QeNPZOOPQ11/DXRRgD", "page_age": null}, {"type": "web_search_result", "title": "National Weather Service", "url": "https://forecast.weather.gov/MapClick.php?lat=37.7771&lon=-122.4196", "encrypted_content": "ErgBCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDPdNpG1oXmpSBoMBNhoMj9hx750OkXsKskdBIjDwrSWsEjExCoOY6Z0/HBKNVZMHH0p/66s1UMdbMbQ3BpcnmdP/LRUQIYLlrj3imgYqPNowYxz7bhOflT7x7YRpg5cB99HVUBYRQYb4f3IpLc5aJvrzOImIq1jsQQqPMcQnnzm6s4v50JT2QwlwnhgD", "page_age": null}, {"type": "web_search_result", "title": "Weather", "url": "https://www.sfchronicle.com/weather/", "encrypted_content": "Eu0CCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDF7furbUQx58vewo3RoMYqiukXR+D6Z8NF+PIjBq9jLlRjra9mYzTHw3eJ7So3MoUbWmbxm74LLukofZuHdxDgFkNOp9PlTtLRW9RGkq8AExzlBbE/0xY+Ey+PS35i0spdIWxLPAL+M9/c9XVil+aeBv45XKZuUGzQZRZr6tkQ9yXcT5K7DvkjiPAKqnyTB9cMgpJ/FRwwgAgCyb2dGZp8RGaO5RkVQ1aohvGQozOO/KqIES/zwp2H+ZoF9f2K35A7y1ygAZfRIPZDINA2pkpc7EiQdeEmZYrMSmg60VoBkdbevq31aptfsLrQWlgMhuY92ipnOz5Gb+hV3m4nAUKdkCSpbfLjKhCJa47ZfV4dARcoxoy3mbkCk/0juVY+7g9me0uft1r1EOfiFx0WEntTPv/AfU81MAYNaWM3gRmzUYAw==", "page_age": null}, {"type": "web_search_result", "title": "Live Doppler 7 | Bay Area Weather News - ABC7 San Francisco", "url": "https://abc7news.com/weather/", "encrypted_content": "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", "page_age": null}]}, {"citations": [{"type": "web_search_result_location", "cited_text": "Home Weather USA San Francisco \u00b7 Now \u00b7 54 \u00b0F \u00b7 Fog. Feels Like: 52 \u00b0F Forecast: 62 / 49 \u00b0F Wind: 7 mph \u2191 from Northeast \u00b7 See more hour-by-hour weathe...", "url": "https://www.timeanddate.com/weather/usa/san-francisco", "title": "Weather for San Francisco, California, USA", "encrypted_index": "EpEBCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDLV3ny+5iG5lj+Fx7xoMg90eCevgTFy1kpsHIjB8h60WGaDH3tqVBssz1eH4O4DrXcbRwGH+FvJLALFXASIBsvxqjpLZU1QRBcxLcLMqFcoibBJqujxbTeS2ipI51b8OckbjkhgE"}], "type": "text", "text": "The current weather in San Francisco is 54\u00b0F with fog, with a forecast high of 62\u00b0F and low of 49\u00b0F. Wind is 7 mph from the northeast."}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 10740, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 122, "service_tier": "standard", "server_tool_use": {"web_search_requests": 1}}}
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_streaming_call_sync_events/9cb114c8-69bd-4111-841b-edee30333afd.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_streaming_call_sync_events/9cb114c8-69bd-4111-841b-edee30333afd.json
deleted file mode 100644
index 0c9f8df6..00000000
--- a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools.test_streaming_call_sync_events/9cb114c8-69bd-4111-841b-edee30333afd.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01919EJZ2QnfiQMaJsEDgX1t\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01ACkkNu59xcFXwQQdfjR5dC\",\"name\":\"get_weather\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"loca\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"tion\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\": \\\"San Fran\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"cisco,\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"units\\\": \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":74} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01Qq1aGWEp7xgSohVeUX4bGc\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":8,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The weather in San Francisco, CA is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" currently **\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Sunny** with a temperature of **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"68°F**.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":25} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
-]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/4a71c9f9-6191-4820-b1a1-89f3bb179078.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/4a71c9f9-6191-4820-b1a1-89f3bb179078.json
new file mode 100644
index 00000000..65ce115c
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/4a71c9f9-6191-4820-b1a1-89f3bb179078.json
@@ -0,0 +1,258 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "781"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What's the weather in San Francisco, New York, London, Tokyo and Paris?If you need to use tools, call only one tool at a time. Wait for the tool’sresponse before making another call. Never call multiple tools at once."
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01WazLBoVU9XD7M54uN8hcGD",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "I'll get the weather for each of these cities. Let me start by checking San Francisco."
+ },
+ {
+ "type": "tool_use",
+ "id": "toolu_01DUC5sKZofEcuFX4NTvXAGp",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 701,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 93,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "1265"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What's the weather in San Francisco, New York, London, Tokyo and Paris?If you need to use tools, call only one tool at a time. Wait for the tool’sresponse before making another call. Never call multiple tools at once."
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "I'll get the weather for each of these cities. Let me start by checking San Francisco.",
+ "type": "text"
+ },
+ {
+ "id": "toolu_01DUC5sKZofEcuFX4NTvXAGp",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01DUC5sKZofEcuFX4NTvXAGp",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01QHdi3sMxCqoFkWw1UapVEw",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "Now let me check New York."
+ },
+ {
+ "type": "tool_use",
+ "id": "toolu_01B3V3NpBkEHenKXsgAW7mhQ",
+ "name": "get_weather",
+ "input": {
+ "location": "New York, NY",
+ "units": "f"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 834,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 81,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6401445a-5758-4777-9778-3008bae873ec.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6401445a-5758-4777-9778-3008bae873ec.json
new file mode 100644
index 00000000..1c9597a7
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6401445a-5758-4777-9778-3008bae873ec.json
@@ -0,0 +1,237 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "390"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What's the weather in SF in Celsius?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_016LXp91BGtikmRAGAeGuddr",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "tool_use",
+ "id": "toolu_01Gioi16VivrVViPseH2cZDu",
+ "name": "get_weather",
+ "input": {
+ "location": "SF",
+ "units": "c"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 597,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 71,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "732"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What's the weather in SF in Celsius?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "id": "toolu_01Gioi16VivrVViPseH2cZDu",
+ "input": {
+ "location": "SF",
+ "units": "c"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01Gioi16VivrVViPseH2cZDu",
+ "content": "{\"location\": \"SF\", \"temperature\": \"20\\u00b0C\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_018wBXcYwLjNFfcwWmQN1uGN",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "The weather in SF is currently **20°C** and **Sunny**!"
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 705,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 20,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6cd089e9-1c08-40a5-851d-c272b3f1c248.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6cd089e9-1c08-40a5-851d-c272b3f1c248.json
new file mode 100644
index 00000000..45bf821a
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6cd089e9-1c08-40a5-851d-c272b3f1c248.json
@@ -0,0 +1,620 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "595"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ }
+ ],
+ "model": "claude-3-5-haiku-latest",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-3-5-haiku-20241022",
+ "id": "msg_01275mRYhdQWbNVMxZjVzshJ",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "I'll help you get the weather for San Francisco. I'll retrieve the temperature in both Fahrenheit and Celsius for you."
+ },
+ {
+ "type": "tool_use",
+ "id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 424,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 101,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "1074"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "I'll help you get the weather for San Francisco. I'll retrieve the temperature in both Fahrenheit and Celsius for you.",
+ "type": "text"
+ },
+ {
+ "id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "content": "RuntimeError('Unexpected error, try again')",
+ "is_error": true
+ }
+ ]
+ }
+ ],
+ "model": "claude-3-5-haiku-latest",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-3-5-haiku-20241022",
+ "id": "msg_01HFNsNvYNRazCCp9L6Ean9Y",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "I apologize, but there seems to be a temporary issue with retrieving the weather information. Let me try again:"
+ },
+ {
+ "type": "tool_use",
+ "id": "toolu_01RPpGTPHZBTK27CruspxqvL",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 554,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 99,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "1583"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "I'll help you get the weather for San Francisco. I'll retrieve the temperature in both Fahrenheit and Celsius for you.",
+ "type": "text"
+ },
+ {
+ "id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "content": "RuntimeError('Unexpected error, try again')",
+ "is_error": true
+ }
+ ]
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "I apologize, but there seems to be a temporary issue with retrieving the weather information. Let me try again:",
+ "type": "text"
+ },
+ {
+ "id": "toolu_01RPpGTPHZBTK27CruspxqvL",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01RPpGTPHZBTK27CruspxqvL",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-3-5-haiku-latest",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-3-5-haiku-20241022",
+ "id": "msg_013dwJgYtm14xyz4VJ8J8gnA",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "Great! Here's the current weather in San Francisco:\n- Temperature: 68°F\n- Condition: Sunny\n\nLet me also get the temperature in Celsius for you:"
+ },
+ {
+ "type": "tool_use",
+ "id": "toolu_014Qf7b4odsWZYj4ngf41v5R",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "c"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 692,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 114,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "2129"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "I'll help you get the weather for San Francisco. I'll retrieve the temperature in both Fahrenheit and Celsius for you.",
+ "type": "text"
+ },
+ {
+ "id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01ASSujsMzFEe4PjhTGzbmdi",
+ "content": "RuntimeError('Unexpected error, try again')",
+ "is_error": true
+ }
+ ]
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "I apologize, but there seems to be a temporary issue with retrieving the weather information. Let me try again:",
+ "type": "text"
+ },
+ {
+ "id": "toolu_01RPpGTPHZBTK27CruspxqvL",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01RPpGTPHZBTK27CruspxqvL",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "text": "Great! Here's the current weather in San Francisco:\n- Temperature: 68°F\n- Condition: Sunny\n\nLet me also get the temperature in Celsius for you:",
+ "type": "text"
+ },
+ {
+ "id": "toolu_014Qf7b4odsWZYj4ngf41v5R",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "c"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_014Qf7b4odsWZYj4ngf41v5R",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"20\\u00b0C\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-3-5-haiku-latest",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-3-5-haiku-20241022",
+ "id": "msg_01BpDU7ntE7GTXZFUv9i4es7",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "- Temperature: 20°C\n\nIt's a beautiful sunny day in San Francisco!"
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 845,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 22,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6f763c87-ecb6-4217-8f6f-32db8e84a6be.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6f763c87-ecb6-4217-8f6f-32db8e84a6be.json
new file mode 100644
index 00000000..7056cdd1
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/6f763c87-ecb6-4217-8f6f-32db8e84a6be.json
@@ -0,0 +1,221 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "175"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "type": "web_search_20250305",
+ "name": "web_search"
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01MGkxozBoPDqy5jbybFMJPb",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "I'll search for the current weather in San Francisco for you."
+ },
+ {
+ "type": "server_tool_use",
+ "id": "srvtoolu_01H4rAobZLG4aVty6fUuEqTk",
+ "name": "web_search",
+ "input": {
+ "query": "San Francisco weather today"
+ }
+ },
+ {
+ "type": "web_search_tool_result",
+ "tool_use_id": "srvtoolu_01H4rAobZLG4aVty6fUuEqTk",
+ "content": [
+ {
+ "type": "web_search_result",
+ "title": "San Francisco, CA Weather Forecast | AccuWeather",
+ "url": "https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629",
+ "encrypted_content": "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",
+ "page_age": "2 weeks ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "San Francisco Bay Area weather forecast – NBC Bay Area",
+ "url": "https://www.nbcbayarea.com/weather/",
+ "encrypted_content": "EscECioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDFSJ0jI9o2K2rBDxqBoMuZrha8vmUDJNmIWoIjAZwCa/eOH5d9IaWWQT9cXz/Kx2EfTbs2V1fF+g/Ft1adsUvHO+R2RHQ5Ewuz+P6RIqygOHnZGH3gC1QJj6S1n82GRV1XfsEud23dh4WaSg6esIjJFyo01AwOiowtg4eg0wWx+XDFYVIJzEU0R76PHf1vnqTxLfNsosKVGIuUFg+aEcjoteagZzg2UKtBFX5q1EbB8XzYb1ZUdMkPFcxSBSdMQkhpKi5XXeg+AJBmh11n9mdToJHpSQs9ETH6W5HtIML1zsWqH8i/eJK8+mmKnvaz6sAZHvyeCunxBTvU8dwHc/724hikjKLmaRUUK2KGoHIZNaaaMzIzaju2kyJV2xKypq099c81mdkCVyFY1u9pqE44Db/dzJTqWZBK5LHrNplgYGQhqwCWUp7Z0QQ0+VmuBHPUPxXRParaK0d6dzc/y5tjDV0fVLme1PxhzZWIj2ynOLKSReQgs0RxMHoJOZs6ZdxCzac81LLdx3IPfdGurvKofFSjf93I/k+kbc2S88PGBQ4wyiELeUYr1fZ22tUa6DjdoAFefKcBdIiewzzSxATLjvHKQbfgPnKr9CsI5vCpq6LI/yDibTOWE4tk5clwianzv35X53rUL/9tcdOyFoeLdmVHRXhtvq0nBFObgQ0/CxsAASlX9qydb0Onh17ZX3Tvsv+94tpKN4BBgD",
+ "page_age": "3 days ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "San Francisco Bay Area, CA",
+ "url": "https://www.weather.gov/mtr",
+ "encrypted_content": "EooLCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDCTT6ISdeT/jSJ2nwBoMKene51/3GlJyBrS2IjBpQUlfApLKsh5wJ8vf5ccLGqpNJR1/0M4QDZuH9flC/wx0MMlqgBMN/oYbpynw4lkqjQogSOsj+B1bWOesmD7FPoeAnUY/R94VOFHK2zsIzwPm/r/ei6zpxLo8aVWENmATaVLNUPVovp1lD68n7cWmLDEOylMzw8zgVnvg+4gBAV5SDopGG8t+IwWZG4WXiRL1pFOIAXmwpZo7DRaJMhyVN4KFx3YZcfjNKRZ6Z6Hh83DsLEZ3Akmw2muf8ZwNqEPjRFxOQ3wAOSp3ez+xcGlRvkGcJiFHe7Di3Je8vMQ3BU31MOtUPfkUVB2Z0CuLdk3CvPOUtJk+i30GxVOi59ZBFndK702pVq6t3hzHpJPBjyljs6O33zadbHNbXZeA/Vj0plYQ1bBPyWygevNHnhu/A+1I6LZcdjrWJRQ4srC0VZdmLK/lFkBLEd3xZVNCTdABX5z414iGMjPNZiL7lgvj8sUk0ZRHf3jfNwdV6elwqovVtLa1aYF8Cg1/+GTkQm1QIZwtvzEn/JnyQysIUbG0ukpvAJTMkkaD4y7h8bHtcZSoktsDnMmO3m46LcmCGqWt4e2J3OT/9sU7rtTqxdbY7eaWl4sJw6OvfKQsvYgMMP6ptRgGPTeeA40USGJ6TErutGwt6tm1lluGc+aA1X8ufJlu7BjJ8/BdPxqT6Bs5JlDrRPWAOssM60eGNUby1DJmpWkSrhfBMgvOwBFfV1ZjS44mMNbbwF6FsRf97sXl1vdZMD2KY7USpJMQPqnGzFCYd+uBnal9hLBVrMFQ91J74fFYSmVtHvW2wXaIRTWdyQJTB816GHDuIjr0TLuTohKOE7mMefYkqBI5WKm+t1DiZGmMYTpkQujGqVHoCwwOOuvs5INv3EDOgj675NNPZPZmfliHfNfyJGd+2aW78jsw3plm5GLjOrogdxa8uFxslDILgldDRBv7IPieXLyphqJcLTLAov6rjB+VtP/z7N1syzTuCzIKc2YtRHPiIfUKGmM1W3JZ6FFUTFt7MT1onKw9LNe/4VGnfZcYLgFJnpunKqlGzaxK2olMRTXUpclJmeR3/P8BHOcbs9WuJz1jNc7TDXHdZQ31lQEWGxbwT82/LQkmo9PoApK/G7lHBeGHGtqC/nAUR/0bfDEZstiC4Tlu7pj6BlPqsrjqUesV4496NkWF/Ot4fu6kls6lwzEAitmMZmH8qt9DlWEK554G5nHkywz4i3iCj+racvgg4FTHMlMSTfcWe/EN2Fm7YuCW8lVcJl7kkEHLKdR2WZZ03VbLDgc8hgMePhcr7pQjIFgRjxWM/nfRO1zQ+dT1Kk/IRs9Y9ZMVZbz8k7LU5owqMjg0N4sSn0pZW+ZQ7G3srSsgyKScE/NY+OsSr1qVvmGTwfLkX4uBGwOYISFw3SF0FZYB14v5yBXs9Y+QK7YfvN/hPJf8Hn7kEAGg5g9uFTZZDE5DD9B5hjsr1eB+E/WSj1APs1jog7YwltF5h56CY25HP5TNAt8PxUWSXnQDlSIZDMR7wbuSlMYMTpo+9Lhv+Oqf71yXexcKK2Q6rEVzi/0WVskIwyB7JixzYxBGMOgx6DPosRLaXBPLAtV0SdblwNU3Pv8aBJsltNScwRI/Tjirb6jGW0mBeY1ewQovAWmfUPZF/XJqN6G5GFyazmyhUvb7DShAw78d+88i/cLngNja3PG2uDxUtd41pj8pAAtp8PVAavy5KHP7izdFj/7teMu2X8h4VzJSAWdPleDkVZpOjyFH7XOn+yrPL48tnmlSxaM8ebOwhmhl/DhuXIfXnksYAw==",
+ "page_age": "2 weeks ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "Live Doppler 7 | Bay Area Weather News - ABC7 San Francisco",
+ "url": "https://abc7news.com/weather/",
+ "encrypted_content": "Eu4ICioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDGyinkRBQZrxDpWaSxoMjtVWfchuKZV2ZujnIjAm7Fyzf9cPKWQxBK9OTagVr5zD+cY7B98L1VzfYN3FAvGbq0ZeQrvIuwKt98t6fzUq8Qcn8bH3zX+3tvz2Y7o4Vstb+TBzQEIBPgsNTTpUajRwD1cFlZ4C8oDGl8mXv7vf7SlAK5F/xi7TGhgbPA4ghFUNAVeL0lv3/9agOIjQW9OBj0329gV6yNpz0X7FtgnbtvS9q6rEdkss9Vz37L98AM7K5SaeavAauhFBvVYWrq+IVn1V0uy47PkgoraUOeBJ4JrI6a/pdTZADHYcpRRWT9udnku4nUSQSW39hnUrWVZUMqTK7vxeVA4vWIuZ9kTC+2gkjf+jk2gWpKGPkVp7KBUQtNKA802Ql6SwajbRWzt7k+Jb4fWa1CP92T545Cu/hQI2D22u+scEikIzet/9nn+VBGvE5JPf8W+hhgnr1hHznP/uoEG8Ju7TAiYswdOoGHV3PwgbMtS1G04kr6RVQkRhqUiohTf3X0CwoIIGmV1jypr75th4mSoJP4ViEFJ8+7HmsVyYi+Ej95rTXYjqd9nrN0/frEFiH9DpUJ7Qi6rwqjvY9YJdbT3hNDn/2YlH57Pi1rW7iWnQDr62IZprq1ER/hUv8giSIsOQrPDLjN5zOuYM+79tazKIglEfnoLvAnfP42yOu3D6rkLBJ/cNbvJgOwJlyVRMcUZBRVPhbp5xUCda6f8CLUyJPqoYz0gFrFiZvl/lXll/aFG/R+prSuAJQPE78Oz/3PPMXBxJtHMRfUzQE1gAtFfQICzuGcv+S3b7nbw+YXhUuB2i7L/UIfOYVIEkc5Y1llaJ4fFmTQYr7XC528dyoN5TZIDdA6em9e2h8sQ0UmCNWhoHa/dVsmCTzJaBhCmA97uWY4JJQM9sn6R1KSRFuHbe5IjkZxtpxJgxX4QOgZ53fQEXygYirWKZYC+jqcaXMXl31OQtVSwXh4xkpR4pgzWYRbw8HsnYk2R92v1p3tkwTS0DAk9eoeB43JRs6Ys/vGUdJWsHeP0qyc5D+bbkeXXEqA8ln41MMzCLmLs+dQZ0FvfLV7fyyrYGJd8camjeSX5ruljlg5vChizSvEV3Z4+WDu76kupsv3zopFTf/T1MAUlPZESoys6QkWe8MzJNJwE0LXqXuW8Wpj3bx11j1MMiu2Y2w6jICrw4HSGReNBBKVYsDNFY1I4aUlhzRdOoISUNhozuwcBl8YG3jgrmkdUkZv0eyodAIIz8n2vG4mt4Cge+PkyC/gurzNY+T+L/XbBL+uqbTN/A5w5EzlGuPgzGvtszZXup8wdw/wHGnywuFdy9L5zCakmk+AW1CWMRNzFc+Whk8M4wswegPlUX7t9stb8vUsRTYJjPFE3RCr9e6T0hHDCBnnMG6FhrTSqhuFXXYZkQWgdH+RZYwZMYgWjmqX19Rqpn0/FCGAM=",
+ "page_age": "6 days ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "San Francisco, CA Hourly Weather Forecast | Weather Underground",
+ "url": "https://www.wunderground.com/hourly/us/ca/san-francisco",
+ "encrypted_content": "EooHCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDJqjYspNmpleEKetaxoMeaD364eVcy1Dv0E0IjB2BFFVsDPzic0xz/7k4HHwwA4kcsDNx4BeLk/0jiymdDo8V0w1hV7eTuZzce/r5WMqjQZ425KPXEja50cqGhec2CIw/7o31zFbBBE6l3iijtRcIcPTlQh6Q0NX9TaAlAE28Uf6JlsEhySrC51qS4qJ08FwaVRP7hYpjJnL+SNWDrsGGKPhMMRSQB6/Mkx2hLKKaILdBcJ0kiD1BjrXjj5kLTWBv7G53qj5shajORgHfmhVYkVtzDPxbnM6x8JDfckvDFArCMIiwtkrBmxCUTgxXYaWQE2FhP3G+ePryj8wBzjyk8Vs/FJoaR/ayheTbG2UfoSzePWyfxUI6Ilhu+/9+GZ2FhxlUOV7SRZuBM0aWIroZWQeOJih+rH6sETurWilNRdbUmbaOJhReTwli3G0OXqUxj61uqoagis9DTGHOUNKDM8OUqxifsRYve8mEmOelKprB2azcedtFNYpwW0H1heTlXcYtFC83p/oOZ7N+g2dEwI9pf1BlktgQqiU4CH95+8ervYCSb98BZlCKI3TsQ2pL6tCVcFmUnpCmu0kODZJBpQyR6aY6rm2N+NpeHC1NCrzNz2GpRax77ZaFIs0ewn3mLVIU+yYC5XlbGwA4G62tACLDCOXM4qdiNm+kqi4TkWxW+D4QDowNUFwb3eIADfvS/9Z6eJcF6Fiq7M6G7UrLLOFKSjV7PxStyX5ZJ3du6nR5MaFIR23HNJYYjFZUnKbSAEM/Q1AynzCcywu++WoJFkwB7iMAFHsy3REvqV22USCPuH2721EQm2D358tlR0d6uPl1eeA3WXs10bmBzWqumOORu+K2lj+sfgeex+LwUMZh8zJM+8DlCQTwbRpBi6Z4MpCx4od5XxL+2OvyHD92wapKk7oa30oM/M30QKgVlDizxnh3d9Jse/tJDucWGhw4laGd8bQVABfndlkyczyZvk5xj+nDEMm1jrXXZ8+Hqm2yoNNF/Ob1FwKWr9Gn5ZiN465OPA3d9qA0wtPUDRbcSDwWlL9M/dnhFhPEtQgf6WL2uXUAyBN/QLMCsFTxhgrxcidai/wndRZjXdjAgBrbXRLS2VHGu8KIJAz6/8fmtGi8ElcI3Fo40zYU41zGAM=",
+ "page_age": null
+ },
+ {
+ "type": "web_search_result",
+ "title": "San Francisco, CA Current Weather - The Weather Network",
+ "url": "https://www.theweathernetwork.com/en/city/us/california/san-francisco/current?_guid_iss_=1",
+ "encrypted_content": "EpwCCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDERaOLj0hp1f2d3pdxoM0Zp89UizgQdTlV8KIjCFUJNONXXhf2Azrvyo7r4Ajux52Pfsummcdpvt+2t5VUydmq43hXOLLGzbc1hdL38qnwG7FS1Ffi0jmoaePNTXWZSPiCZ9C9H5q/cm39dQl4DD4EkhP6Bs3Lqp8wMrWMGUXrUEfdhsKUVA/RhjJ3JcMfQEEWhBAs6+30RdLYzl2VaqRJNjHhFC3nkvUgsld5j4sC+5Y7jcIEqZQfCW2BDJTomziiSUevT8nBqQYRO3w97I/lk9O10a7aDblp0+pSk4KSJkntNFT2tpyUIelQpoLu8YAw==",
+ "page_age": "2 weeks ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "San Francisco, CA Weather Forecast, Conditions, and Maps – Yahoo Weather",
+ "url": "https://weather.yahoo.com/us/ca/san-francisco",
+ "encrypted_content": "EswBCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDPJ9RfbpDzf5kJxjcxoMHs4sqi4AH1av+haiIjDwPMwJGgSBWeyKxY0OBjTs4Mtss0DNG0STPbPHr0zJXL4mCloNdWfPEJNX2jKMWbAqUN2MnCBU9rCqDB1Da7+XPbgIGo7Ua3J1CEIy3H2wgS3Sgub98ELQMdLzCfjTRUs4qsLJg7QmHzVZqi6RNUO0qJkdwNd6H58ayrlhA/gh3hlxGAM=",
+ "page_age": "2 weeks ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "San Francisco, CA 10-Day Weather Forecast | Weather Underground",
+ "url": "https://www.wunderground.com/forecast/us/ca/san-francisco",
+ "encrypted_content": "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",
+ "page_age": null
+ },
+ {
+ "type": "web_search_result",
+ "title": "National Weather Service",
+ "url": "https://forecast.weather.gov/MapClick.php?lat=37.7771&lon=-122.4196",
+ "encrypted_content": "EpoICioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDMYOcmEg/FuB6iIBCxoMXD+eYmbQ32fGHEnfIjCuJ753WtDp74k2DWRwVt0+7FFfJGy5rAieczvpxE1vCjdzf7GBl5x27cDkQBmqH/cqnQefd6vHu0LIrNB/h+/BGVaILcd5eukoOEaCy61gcOOOflFOMz++6NNL7jRRs6nH5yBWCFHfQJ5umWpDjZTYyl2bButUJcQyOEd1THIF7M7RFrN8DiLaROHBP52DFvcr9ZgWkJNoTvRI9KTnHIEhh5uYkRT3tqE/uIhjUaRzOa6WXNwUn9/sMmMKfsiqE1qN5R1fhH+dUGDvpAtiqVR5HS0VOaq37BmLq2uSPCloQ5IYzsSIC81OJzJQ7h13JuCiwQ53MTzdWgSzoCdySfZZidHh/kQKW25yLvbSMMQsIS/KGVOvvrYXuN94V45IprQx0bV3n3lp+aXWQO4jlSuSzaH8gSOFxt40iEijfVpTv+WX2Px5rDJtu7XAv6XZKKA7kpunXQTbUkYjgbiUsINWLCrc/t3aPgHy+s4yifptYYWWSzTN6tRw1QYHzEr+OXTcPW4BolbwmoCwMKztIljwVZt3deMi+Rq6fJ/yx2gAUdPjAdkA1PHk26VkfEDqOs0AryfbCwlLT7GKL1nyUXvaqah4F2vfNvSFW0wz1Ne1fpUrHdGGFi+3FlnXqZOboaJeqJAFX/8TIXguXjGgVznf/A2eEqv/Pya+L5IQZti1MD7B1Y6ttuy2fWbIFwVQbsd2w0ES2Rs6+07a+dQWKawJfSD8qwq57dakmgUGJFYQ6ES5TElIIIQpz+CGp0UzvZT52EcLPQy1CwntryyPdc2jmZvfJbZX8iC63I4vTc5uBiM8QLGJrbdhX5q/ZB4DlfN7/VlP9BvckPuwMsNKcuwe6SVnEQJVG5CIjv1A7xUNxAkLYp5GpFvNNI63B2ekCrnFG9Q77+DAGY82zvQsMBQmZ9EUqYPdGOJBZbfQjsaVxxjOCQ9/mJMWd1OlzCdk1a0V63VIhbDbSTmVb7WnJCRoQzPzXvV+yrNWfPyb1TDwSnj9UCqQoZMTVD+5xcAVe8baRxJ4Y7dLsQO/Jy1hgHojYS/56GsGWY71EG1SNpxhU7IT/BjILQKwD5Ep31XGRoOlecb0extPpPMzxTyW6RQjlodSNqLugdKLO6AOmEwzLlxyGYG3E8YA45VGkbkfRsEhGvUO8VSZNovRr/xJYYX0Z8S+2K1VFdrKZLt2MxAGvw39f4RqjnrCBBdYxWZhv/HqD/FxNWhQD7oIjb6qmgrBe+ClE4/sT3h6BoTpUohpOPkT0R/QgctMB/+bJ5GRUmR/gGxvgGo7xioI6L6otadMGAM=",
+ "page_age": "2 weeks ago"
+ },
+ {
+ "type": "web_search_result",
+ "title": "Weather",
+ "url": "https://www.sfchronicle.com/weather/",
+ "encrypted_content": "Ep0FCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDGevzcC7UzIfLJ81UhoMvGN6KO4xjGOrd1iCIjAdD4yMW8M9FCsvN6UgF5QiPheayABNkmgxexO0Os31VFiLApD8owM7QIvzZwLkBGIqoAS5RP5o8vvmvSiWhJEeUTdsJnmrMI52gxnkVpoMbeVJCWtZAj3tsokyvkheMYKzJ3MppFNV3ovAQ/H0O3ttHdtlEbmywifMCBiC+TeuIRFz4DMUWvIuNQSpfCrhB0UZY8/FzlfY+I1cHh/w4/J/Vy0TJDMs22DQ4iqtBje03xBIdMN7P8+xKNpws+rduchyzlpmETzmtIxV8C+YTwIKO5dKpF3vZtE30RS501M4jDMIFUBHksv/DjDPL+6bewDiGpuaemhLYcbjrDMN0BCNjaG6FXLgeeiCXYyLvpZ+SrZjx+zA6AVBsuvLMLywyOylqZf1hiWTZYXR+BOIo65XiURie0N2vabGmtZPf2JnAouEooztjXkod2hci5ONa2eFEIRzcvYp3LHW2/E6F4TTCkZqrvDpq/IvBrh+Fm/Go6x+qZViALxrKwCFtgDAAgVCFBZ/dHUF7AmJGegq65tIt4NzbnnzfKcGQ6+i/PfIVIAFrQNg1hPddFxKyqZLfDGvBFLyM2+E9nmXQ7vUTzRNVE1RRPFtI4iZ3UpSV8fDuQVJ0tfvjxelAsN+xFRweAJMttF8AF/Dm50N9YAGt58ajyhHzUSDPvOu/RDgmhTDhmBjAN1+tTv2Z/PI6N1X8C3AIxAuXINbmlJzRRw8qD4Dwp3DBk9ms2/zBvZx4QIrUpvy6KgopM7hTibTaXq6jkp8tWHCiYZA8BzTy11shM9/aaPrGAM=",
+ "page_age": "4 days ago"
+ }
+ ]
+ },
+ {
+ "type": "text",
+ "text": "Based on the latest weather information for San Francisco:\n\n"
+ },
+ {
+ "citations": [
+ {
+ "type": "web_search_result_location",
+ "cited_text": "... Mostly cloudy, then gradually becoming sunny, with a high near 53. North wind 6 to 9 mph. ",
+ "url": "https://forecast.weather.gov/MapClick.php?lat=37.7771&lon=-122.4196",
+ "title": "National Weather Service",
+ "encrypted_index": "EpEBCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDMIBBVqNadugmkTbaBoMs553Ug3Vho9D3eCTIjCfbTEWsDAyX9i2khTVn/XNMbBM9f9huTgA9oHpYQ9NUvoIrXd8t79PlGQgxQkZGnQqFT9D3bkKUW4ig+qm+PuCZUlrTIVDFxgE"
+ }
+ ],
+ "type": "text",
+ "text": "Today is mostly cloudy becoming sunny, with a high near 53°F and north winds of 6 to 9 mph."
+ },
+ {
+ "type": "text",
+ "text": " "
+ },
+ {
+ "citations": [
+ {
+ "type": "web_search_result_location",
+ "cited_text": "... Patchy fog after 1am. Otherwise, increasing clouds, with a low around 51. West wind around 6 mph becoming calm after midnight. ",
+ "url": "https://forecast.weather.gov/MapClick.php?lat=37.7771&lon=-122.4196",
+ "title": "National Weather Service",
+ "encrypted_index": "EpIBCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDHV+VV5Q7nPfdMZFphoMpldJI+k3ybGJX49mIjBjlbHreJdOhOW3sdCBiNI/riufEek0c38POv6UD4WumGjbHTp2espS7yMTHXWQi6wqFjwtuHHywcl5v/QUQw6LN9yFupcGX0AYBA=="
+ }
+ ],
+ "type": "text",
+ "text": "Tonight will have patchy fog after 1am with increasing clouds and a low around 51°F, with west winds becoming calm after midnight."
+ },
+ {
+ "type": "text",
+ "text": "\n\n"
+ },
+ {
+ "citations": [
+ {
+ "type": "web_search_result_location",
+ "cited_text": "Rain likely, mainly after 4pm. Patchy fog before 1pm. Otherwise, mostly cloudy, with a high near 61. Calm wind becoming west around 6 mph in the after...",
+ "url": "https://forecast.weather.gov/MapClick.php?lat=37.7771&lon=-122.4196",
+ "title": "National Weather Service",
+ "encrypted_index": "EpMBCioIChgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDBtf9on2TcUs4b5m3RoMWAxpMenGV3ROH7v7IjCeRMgMeBvMGQPuEgySrmc6fW+VoCttA0DR7QtUsA8L1t+96LuIVqGB9ggkUU6S1BsqF2N0JIVyO+uWrBI6Euuu58JTXtOeRzr/GAQ="
+ }
+ ],
+ "type": "text",
+ "text": "Tomorrow looks rainy, mainly after 4pm, with patchy fog before 1pm and a high near 61°F, with a 60% chance of precipitation."
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 9360,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 257,
+ "service_tier": "standard",
+ "server_tool_use": {
+ "web_search_requests": 1
+ }
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/ad005e89-f72b-4f0e-941a-9ec3473c1bc8.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/ad005e89-f72b-4f0e-941a-9ec3473c1bc8.json
new file mode 100644
index 00000000..bca3569d
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/ad005e89-f72b-4f0e-941a-9ec3473c1bc8.json
@@ -0,0 +1,241 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "588"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01SFtccfdwXnkEeTTntY4wrA",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "tool_use",
+ "id": "toolu_018iSyDrY8EQQAH313R6xCkA",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 656,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 74,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "960"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "id": "toolu_018iSyDrY8EQQAH313R6xCkA",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_018iSyDrY8EQQAH313R6xCkA",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_016diNmBsVwjnEodFdXMgg4j",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "The weather in San Francisco is currently **sunny** with a temperature of **68°F** (20°C)."
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 770,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 27,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/e4c374b8-ae08-48f3-96e5-9d28066820b0.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/e4c374b8-ae08-48f3-96e5-9d28066820b0.json
new file mode 100644
index 00000000..9886a759
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/e4c374b8-ae08-48f3-96e5-9d28066820b0.json
@@ -0,0 +1,182 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "530"
+ },
+ "body": {
+ "max_tokens": 4000,
+ "messages": [
+ {
+ "role": "user",
+ "content": "Write a detailed 500 word essay about dogs, cats, and birds. Call the tool submit_analysis with the information about all three animals. Note that you should call it only once at the end of your essay."
+ }
+ ],
+ "model": "claude-sonnet-4-5",
+ "tools": [
+ {
+ "name": "submit_analysis",
+ "description": "Call this LAST with your final analysis.",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "summary": {
+ "title": "Summary",
+ "type": "string"
+ }
+ },
+ "required": [
+ "summary"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-sonnet-4-5-20250929",
+ "id": "msg_015MZWkRKxG26Adm8rrYp2YL",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "# A Comparative Look at Three Beloved Companions: Dogs, Cats, and Birds\n\nThroughout human history, animals have played integral roles in our lives, serving as companions, helpers, and sources of joy. Among the most popular pets worldwide are dogs, cats, and birds, each offering unique characteristics and forming distinct relationships with their human caregivers.\n\n## Dogs: Loyal Companions\n\nDogs, often called \"man's best friend,\" have been domesticated for thousands of years, with archaeological evidence suggesting this partnership began over 15,000 years ago. Known for their unwavering loyalty and affectionate nature, dogs have evolved alongside humans to become remarkably attuned to our emotions and behaviors. They possess an extraordinary sense of smell, hearing far superior to humans, and an innate desire to please their owners.\n\nDogs come in hundreds of breeds, ranging from tiny Chihuahuas weighing just a few pounds to massive Great Danes standing over thirty inches tall. This diversity allows people to choose companions that fit their lifestyle, whether they need an energetic running partner, a gentle family dog, or a working animal for herding or protection. Dogs are highly trainable and social creatures, thriving on interaction and requiring regular exercise and mental stimulation. Their pack mentality makes them naturally inclined to form strong bonds with their human families, often displaying protective behaviors and genuine excitement at their owners' return home.\n\n## Cats: Independent Spirits\n\nCats represent a different approach to companionship. Domesticated approximately 10,000 years ago, these elegant felines maintain a reputation for independence while still forming deep attachments to their human companions. Unlike dogs, cats are typically more self-sufficient, content to entertain themselves and requiring less constant attention. This makes them ideal for people with busy schedules or smaller living spaces.\n\nCats are natural hunters, possessing remarkable agility, excellent night vision, and retractable claws that aid in climbing and catching prey. They communicate through various vocalizations, body language, and the famous purr—a sound that often indicates contentment but can also serve other purposes. Cats spend significant portions of their day grooming themselves, making them exceptionally clean animals. While they may seem aloof, cats often display affection on their own terms, seeking out their owners for cuddles and play when the mood strikes them.\n\n## Birds: Vibrant and Vocal\n\nBirds offer yet another dimension to pet ownership. From small finches and canaries to larger parrots and cockatoos, avian companions bring color, song, and remarkable intelligence into homes. Many bird species, particularly parrots, possess impressive cognitive abilities, capable of learning extensive vocabularies, solving puzzles, and even demonstrating emotional awareness.\n\nBirds require specialized care, including appropriate caging, specific diets, and mental enrichment to prevent boredom. Social species like parakeets and cockatiels thrive on interaction, while some birds can live for decades, representing a long-term commitment. Their ability to mimic sounds and human speech fascinates owners, and their playful antics provide endless entertainment.\n\n## Conclusion\n\nEach of these animals—dogs, cats, and birds—offers unique benefits and challenges. Dogs provide active companionship and loyalty, cats offer low-maintenance affection and independence, and birds bring beauty and intelligence in compact packages. The choice between them ultimately depends on individual lifestyle, living situation, and personal preferences, but all three have proven themselves worthy companions throughout human civilization."
+ },
+ {
+ "type": "tool_use",
+ "id": "toolu_01TGC8C88xDaCfXuuHhQqo8j",
+ "name": "submit_analysis",
+ "input": {
+ "summary": "This essay explores three popular pet categories: dogs, cats, and birds. Dogs are characterized as loyal, highly trainable companions domesticated over 15,000 years ago, available in hundreds of breeds with varying sizes and temperaments, requiring regular exercise and social interaction due to their pack mentality. Cats are presented as independent yet affectionate pets domesticated approximately 10,000 years ago, known for their self-sufficiency, hunting abilities, cleanliness, and suitability for smaller living spaces or busy owners. Birds, ranging from small finches to large parrots, are described as intelligent and vocal companions requiring specialized care, capable of mimicking speech, solving puzzles, and providing long-term companionship with some species living for decades. The essay concludes that each animal type offers distinct advantages—dogs provide active loyalty, cats offer low-maintenance independence, and birds bring beauty and intelligence—making the choice dependent on individual lifestyle and preferences."
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 617,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 992,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "compaction",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "1666"
+ },
+ "body": {
+ "max_tokens": 4000,
+ "messages": [
+ {
+ "role": "user",
+ "content": "Write a detailed 500 word essay about dogs, cats, and birds. Call the tool submit_analysis with the information about all three animals. Note that you should call it only once at the end of your essay."
+ },
+ {
+ "role": "user",
+ "content": "You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include:\n1. Task Overview\nThe user's core request and success criteria\nAny clarifications or constraints they specified\n2. Current State\nWhat has been completed so far\nFiles created, modified, or analyzed (with paths if relevant)\nKey outputs or artifacts produced\n3. Important Discoveries\nTechnical constraints or requirements uncovered\nDecisions made and their rationale\nErrors encountered and how they were resolved\nWhat approaches were tried that didn't work (and why)\n4. Next Steps\nSpecific actions needed to complete the task\nAny blockers or open questions to resolve\nPriority order if multiple steps remain\n5. Context to Preserve\nUser preferences or style requirements\nDomain-specific details that aren't obvious\nAny promises made to the user\nBe concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes. Write in a way that enables immediate resumption of the task.\nWrap your summary in tags."
+ }
+ ],
+ "model": "claude-sonnet-4-5"
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-sonnet-4-5-20250929",
+ "id": "msg_01QJzgcThZBbu3o4RMRKWYAz",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "\n## 1. Task Overview\nThe user requested a detailed 500-word essay about dogs, cats, and birds, followed by a single call to a tool named `submit_analysis` with information about all three animals. Key requirements:\n- Essay must be approximately 500 words\n- Must cover all three animal types: dogs, cats, and birds\n- Must call `submit_analysis` tool exactly once at the end\n- The tool call should contain information about all three animals\n\n## 2. Current State\n**Completed:** Nothing has been completed yet. This appears to be the initial state of the task.\n\n**Not yet done:**\n- The 500-word essay has not been written\n- The `submit_analysis` tool has not been called\n\n## 3. Important Discoveries\n**Constraints identified:**\n- The tool `submit_analysis` must be called only once, not separately for each animal\n- The tool call should occur after the essay is complete\n- Need to determine the proper parameters/format for `submit_analysis` tool (this information was not provided in the initial request)\n\n**Uncertainty:**\n- The exact structure and parameters expected by `submit_analysis` are unclear. Will need to infer or use reasonable structure when calling the tool (e.g., might expect JSON with animal names and their characteristics, or separate parameters for each animal)\n\n## 4. Next Steps\n1. **Write the 500-word essay** covering:\n - Dogs (characteristics, behavior, relationship with humans)\n - Cats (characteristics, behavior, relationship with humans)\n - Birds (characteristics, behavior, varieties/examples)\n - Make it detailed and well-structured\n\n2. **Call submit_analysis tool once** after the essay with comprehensive information about all three animals. Since the tool structure wasn't specified, use a logical format such as:\n - Pass information about dogs, cats, and birds as parameters\n - Could structure as JSON or separate arguments depending on tool requirements\n - Include key facts and characteristics for each animal type\n\n## 5. Context to Preserve\n- User specifically emphasized calling the tool \"only once at the end\"\n- Essay should be \"detailed\" - not just superficial information\n- Word count target is approximately 500 words\n- All three animals must be covered adequately in the essay\n- The tool call is a critical component - not optional\n"
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 324,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 525,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/f1c3350a-cdb1-4508-a208-18544b825a9e.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/f1c3350a-cdb1-4508-a208-18544b825a9e.json
new file mode 100644
index 00000000..8d52e50e
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/f1c3350a-cdb1-4508-a208-18544b825a9e.json
@@ -0,0 +1,241 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "598"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What's the weather in SF in Celsius?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01RbvNtiaJHKHJF3hmfMWMRU",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "tool_use",
+ "id": "toolu_01RVA12CSF1d8nekhZFR7Bcb",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "c"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 659,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 74,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "954"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What's the weather in SF in Celsius?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "id": "toolu_01RVA12CSF1d8nekhZFR7Bcb",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "c"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "tool_use_id": "toolu_01RVA12CSF1d8nekhZFR7Bcb",
+ "content": "The weather in San Francisco, CA is currently sunny with a temperature of 20°C.",
+ "type": "tool_result"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01Mp3aR278QuTCzCTtZNHAxD",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "The weather in San Francisco, CA is currently **sunny** with a temperature of **20°C** (about 68°F)."
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 763,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 31,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/f27add0c-5771-4452-8ed9-996f5d74ef77.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/f27add0c-5771-4452-8ed9-996f5d74ef77.json
new file mode 100644
index 00000000..462f3fa6
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/f27add0c-5771-4452-8ed9-996f5d74ef77.json
@@ -0,0 +1,194 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "NOT_GIVEN",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper-method": "stream",
+ "x-stainless-stream-helper": "beta.messages",
+ "x-stainless-helper": "BetaToolRunner",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "602"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ],
+ "stream": true
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "text/event-stream; charset=utf-8",
+ "connection": "keep-alive",
+ "server": "cloudflare",
+ "cf-cache-status": "DYNAMIC",
+ "cache-control": "no-cache",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none"
+ },
+ "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01P2vWV2BG9GRvKe41x6dini\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_014Qh2h4XRw7uSc8sXfxWQQu\",\"name\":\"get_weather\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"loc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"ation\\\": \\\"San\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" Fra\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"ncisco, CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"units\\\":\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" \\\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":74} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "NOT_GIVEN",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper-method": "stream",
+ "x-stainless-stream-helper": "beta.messages",
+ "x-stainless-helper": "BetaToolRunner",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "974"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "id": "toolu_014Qh2h4XRw7uSc8sXfxWQQu",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_014Qh2h4XRw7uSc8sXfxWQQu",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ],
+ "stream": true
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "text/event-stream; charset=utf-8",
+ "connection": "keep-alive",
+ "server": "cloudflare",
+ "cf-cache-status": "DYNAMIC",
+ "cache-control": "no-cache",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none"
+ },
+ "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01PVjGt8uRgCkDMqbEWVyv53\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" weather in San Francisco, CA is currently\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Sunny**\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" with a temperature of **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"68°F**.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":25} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/ff848716-2309-477f-8e90-39877809aaad.json b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/ff848716-2309-477f-8e90-39877809aaad.json
new file mode 100644
index 00000000..fc9d0d2f
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/ff848716-2309-477f-8e90-39877809aaad.json
@@ -0,0 +1,194 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "NOT_GIVEN",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper-method": "stream",
+ "x-stainless-stream-helper": "beta.messages",
+ "x-stainless-helper": "BetaToolRunner",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "602"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ],
+ "stream": true
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "text/event-stream; charset=utf-8",
+ "connection": "keep-alive",
+ "server": "cloudflare",
+ "cf-cache-status": "DYNAMIC",
+ "cache-control": "no-cache",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none"
+ },
+ "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01BtTXWuDXFU7AoXqmXhteHs\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01Sx7cuHYwHAkqw2MogXQQ1d\",\"name\":\"get_weather\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"locat\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"ion\\\": \\\"San F\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"rancisco\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", CA\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"units\\\": \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":74} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "NOT_GIVEN",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "Anthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "false",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper-method": "stream",
+ "x-stainless-stream-helper": "beta.messages",
+ "x-stainless-helper": "BetaToolRunner",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "974"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "id": "toolu_01Sx7cuHYwHAkqw2MogXQQ1d",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01Sx7cuHYwHAkqw2MogXQQ1d",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ],
+ "stream": true
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "text/event-stream; charset=utf-8",
+ "connection": "keep-alive",
+ "server": "cloudflare",
+ "cf-cache-status": "DYNAMIC",
+ "cache-control": "no-cache",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none"
+ },
+ "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_0112kkkttNuK7GZWAFeFrBWm\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":8,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The weather in San Francisco, CA is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" currently\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Sunny** with a temperature of **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"68°F**.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":25} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/__module__/c4061e82-ea59-4756-9549-71a35da24499.json b/tests/lib/tools/__inline_snapshot__/test_runners/__module__/c4061e82-ea59-4756-9549-71a35da24499.json
new file mode 100644
index 00000000..4ccf59c1
--- /dev/null
+++ b/tests/lib/tools/__inline_snapshot__/test_runners/__module__/c4061e82-ea59-4756-9549-71a35da24499.json
@@ -0,0 +1,241 @@
+[
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "AsyncAnthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "async:asyncio",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "588"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01PmS112jMusZPxxKbGiWgfg",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "tool_use",
+ "id": "toolu_01UNskverbGHRXGrdU8Es5kA",
+ "name": "get_weather",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 656,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 74,
+ "service_tier": "standard"
+ }
+ }
+ }
+ },
+ {
+ "request": {
+ "method": "POST",
+ "url": "https://api.anthropic.com/v1/messages?beta=true",
+ "headers": {
+ "host": "api.anthropic.com",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "x-stainless-timeout": "600",
+ "accept": "application/json",
+ "content-type": "application/json",
+ "user-agent": "AsyncAnthropic/Python 0.75.0",
+ "x-stainless-lang": "python",
+ "x-stainless-package-version": "0.75.0",
+ "x-stainless-os": "MacOS",
+ "x-stainless-arch": "arm64",
+ "x-stainless-runtime": "CPython",
+ "x-stainless-runtime-version": "3.9.18",
+ "x-stainless-async": "async:asyncio",
+ "anthropic-version": "2023-06-01",
+ "x-stainless-helper": "BetaToolRunner",
+ "anthropic-beta": "structured-outputs-2025-11-13",
+ "x-stainless-retry-count": "0",
+ "x-stainless-read-timeout": "600",
+ "content-length": "960"
+ },
+ "body": {
+ "max_tokens": 1024,
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather in SF?"
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {
+ "id": "toolu_01UNskverbGHRXGrdU8Es5kA",
+ "input": {
+ "location": "San Francisco, CA",
+ "units": "f"
+ },
+ "name": "get_weather",
+ "type": "tool_use"
+ }
+ ]
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "tool_result",
+ "tool_use_id": "toolu_01UNskverbGHRXGrdU8Es5kA",
+ "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}"
+ }
+ ]
+ }
+ ],
+ "model": "claude-haiku-4-5",
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Lookup the weather for a given city in either celsius or fahrenheit",
+ "input_schema": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "The city and state, e.g. San Francisco, CA",
+ "title": "Location",
+ "type": "string"
+ },
+ "units": {
+ "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit",
+ "enum": [
+ "c",
+ "f"
+ ],
+ "title": "Units",
+ "type": "string"
+ }
+ },
+ "required": [
+ "location",
+ "units"
+ ],
+ "type": "object"
+ }
+ }
+ ]
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "content-type": "application/json",
+ "connection": "keep-alive",
+ "cf-cache-status": "DYNAMIC",
+ "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
+ "vary": "Accept-Encoding",
+ "x-robots-tag": "none",
+ "server": "cloudflare"
+ },
+ "body": {
+ "model": "claude-haiku-4-5-20251001",
+ "id": "msg_01VTYCVKcYrrFfXDrjSpQRnW",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "The weather in San Francisco, CA is currently **68°F and Sunny**. Great day to be outside!"
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 770,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "cache_creation": {
+ "ephemeral_5m_input_tokens": 0,
+ "ephemeral_1h_input_tokens": 0
+ },
+ "output_tokens": 27,
+ "service_tier": "standard"
+ }
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/lib/tools/__inline_snapshot__/test_runners/test_streaming_call_sync_events/9cb114c8-69bd-4111-841b-edee30333afd.json b/tests/lib/tools/__inline_snapshot__/test_runners/test_streaming_call_sync_events/9cb114c8-69bd-4111-841b-edee30333afd.json
deleted file mode 100644
index 745be1e3..00000000
--- a/tests/lib/tools/__inline_snapshot__/test_runners/test_streaming_call_sync_events/9cb114c8-69bd-4111-841b-edee30333afd.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01DfDUn6fFzYyGNjmWmA9XVK\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-3-5-sonnet-20241022\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":473,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I'll\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" help you check the weather in San Francisco\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\". Since the location parameter needs to be more\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" specific, I'll use \\\"San Francisco, CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" and I'll show you the temperature in both\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Celsius and Fahrenheit.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01QYZ2sgjBq1oG8iryEt8QjR\",\"name\":\"get_weather\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"location\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\": \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"San Francisc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"o, CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"unit\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"s\\\":\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" \\\"c\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01Lu9hbRKgrZqvHFutZxCNnZ\",\"name\":\"get_weather\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"loc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"at\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"io\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"n\\\": \\\"San \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"Francisco\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"units\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\": \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":473,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":176} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
- "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_014L4v4PaF8RoEXeryKARnCp\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-3-5-sonnet-20241022\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":767,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" weather in San Francisco, CA is\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" currently sunny with a temperature of 20\u00b0C (68\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\u00b0F).\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":767,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":27} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"
-]
\ No newline at end of file
diff --git a/tests/lib/tools/test_runners.py b/tests/lib/tools/test_runners.py
index 7575deba..8b5ed903 100644
--- a/tests/lib/tools/test_runners.py
+++ b/tests/lib/tools/test_runners.py
@@ -1,10 +1,9 @@
import json
import logging
-from typing import Any, Dict, List, Union
+from typing import Any, Dict, List, Union, cast
from typing_extensions import Literal, TypeVar
import pytest
-from respx import MockRouter
from inline_snapshot import external, snapshot
from anthropic import Anthropic, AsyncAnthropic, beta_tool, beta_async_tool
@@ -17,68 +16,21 @@
from anthropic.types.beta.beta_tool_result_block_param import BetaToolResultBlockParam
from ..utils import print_obj
-from ...conftest import base_url
-from ..snapshots import make_snapshot_request, make_async_snapshot_request, make_stream_snapshot_request
_T = TypeVar("_T")
-# all the snapshots in this file are auto-generated from the live API
-#
+# all the snapshots in this file are auto-generated from the live API,
# you can update them with
-#
-# `ANTHROPIC_LIVE=1 ./scripts/test --inline-snapshot=fix -n0`
-
-snapshots = {
- "basic": {
- "responses": snapshot(
- [
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_0133AjAuLSKXatUZqNkpALPx", "type": "message", "role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01DGiQScbZKPwUBYN79rFUb8", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "f"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 74, "service_tier": "standard"}}',
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_014x2Sxq2p6sewFyUbJp8Mg3", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "The weather in San Francisco, CA is currently **68\\u00b0F** and **Sunny**. It\'s a nice day! \\u2600\\ufe0f"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 770, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 33, "service_tier": "standard"}}',
- ]
- ),
- "result": snapshot(
- "ParsedBetaMessage(container=None, content=[ParsedBetaTextBlock(citations=None, parsed_output=None, text=\"The weather in San Francisco, CA is currently **68°F** and **Sunny**. It's a nice day! ☀️\", type='text')], context_management=None, id='msg_014x2Sxq2p6sewFyUbJp8Mg3', model='claude-haiku-4-5-20251001', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, input_tokens=770, output_tokens=33, server_tool_use=None, service_tier='standard'))\n"
- ),
- },
- "custom": {
- "responses": snapshot(
- [
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_01FKEKbzbqHmJv5ozwH7tz99", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "Let me check the weather for San Francisco for you in Celsius."}, {"type": "tool_use", "id": "toolu_01MxFFv4azdWzubHT3dXurMY", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "c"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 659, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 88, "service_tier": "standard"}}',
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_01DSPL7PHKQYTe9VAFkHzsA3", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "The weather in San Francisco, CA is currently **20\\u00b0C** and **Sunny**. Nice weather!"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 787, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 26, "service_tier": "standard"}}',
- ]
- ),
- "result": snapshot(
- "ParsedBetaMessage(container=None, content=[ParsedBetaTextBlock(citations=None, parsed_output=None, text='The weather in San Francisco, CA is currently **20°C** and **Sunny**. Nice weather!', type='text')], context_management=None, id='msg_01DSPL7PHKQYTe9VAFkHzsA3', model='claude-haiku-4-5-20251001', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, input_tokens=787, output_tokens=26, server_tool_use=None, service_tier='standard'))\n"
- ),
- },
- "streaming": {
- "result": snapshot(
- "ParsedBetaMessage(container=None, content=[ParsedBetaTextBlock(citations=None, parsed_output=None, text='The weather in San Francisco, CA is currently **Sunny** with a temperature of **68°F**.', type='text')], context_management=None, id='msg_01Vm8Ddgc8qm4iuUSKbf6jku', model='claude-haiku-4-5-20251001', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, input_tokens=781, output_tokens=25, server_tool_use=None, service_tier='standard'))\n"
- )
- },
- "tool_call": {
- "responses": snapshot(
- [
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_01NzLkujbJ7VQgzNHFx76Ab4", "type": "message", "role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01SPe52JjANtJDVJ5yUZj4jz", "name": "get_weather", "input": {"location": "SF", "units": "c"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 597, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 71, "service_tier": "standard"}}',
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_016bjf5SAczxp28ES4yX7Z7U", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "The weather in SF (San Francisco) is currently **20\\u00b0C** and **sunny**!"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 705, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 23, "service_tier": "standard"}}',
- ]
- ),
- },
- "tool_call_error": {
- "responses": snapshot(
- [
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_01QhmJFoA3mxD2mxPFnjLHrT", "type": "message", "role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01Do4cDVNxt51EuosKoxdmii", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "f"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 74, "service_tier": "standard"}}',
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_0137FupJYD4A3Mc6jUUxKpU6", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "I apologize, but I encountered an error when trying to fetch the weather for San Francisco. This appears to be a temporary issue with the weather service. Could you please try again in a moment, or let me know if you\'d like me to attempt the lookup again?"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 760, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 58, "service_tier": "standard"}}',
- ]
- )
- },
-}
+# `./scripts/test --inline-snapshot=fix -n0 --http-record`
@pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1")
class TestSyncRunTools:
- @pytest.mark.respx(base_url=base_url)
- def test_basic_call_sync(self, client: Anthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:ad005e89-f72b-4f0e-941a-9ec3473c1bc8.json"))],
+ )
+ def test_basic_call_sync(self, snapshot_client: Anthropic) -> None:
@beta_tool
def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType:
"""Lookup the weather for a given city in either celsius or fahrenheit
@@ -91,27 +43,54 @@ def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResu
"""
return json.dumps(_get_weather(location, units))
- message = make_snapshot_request(
- lambda c: c.beta.messages.tool_runner(
- max_tokens=1024,
- model="claude-haiku-4-5",
- tools=[get_weather],
- messages=[{"role": "user", "content": "What is the weather in SF?"}],
- ).until_done(),
- content_snapshot=snapshots["basic"]["responses"],
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
+ message = snapshot_client.beta.messages.tool_runner(
+ max_tokens=1024,
+ model="claude-haiku-4-5",
+ tools=[get_weather],
+ messages=[{"role": "user", "content": "What is the weather in SF?"}],
+ ).until_done()
+
+ assert print_obj(message) == snapshot(
+ """\
+ParsedBetaMessage(
+ container=None,
+ content=[
+ ParsedBetaTextBlock(
+ citations=None,
+ parsed_output=None,
+ text='The weather in San Francisco is currently **sunny** with a temperature of **68°F** (20°C).',
+ type='text'
+ )
+ ],
+ context_management=None,
+ id='msg_016diNmBsVwjnEodFdXMgg4j',
+ model='claude-haiku-4-5-20251001',
+ role='assistant',
+ stop_reason='end_turn',
+ stop_sequence=None,
+ type='message',
+ usage=BetaUsage(
+ cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0),
+ cache_creation_input_tokens=0,
+ cache_read_input_tokens=0,
+ input_tokens=770,
+ output_tokens=27,
+ server_tool_use=None,
+ service_tier='standard'
+ )
+)
+"""
)
- assert print_obj(message, monkeypatch) == snapshots["basic"]["result"]
-
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [
+ cast(Any, external("uuid:6cd089e9-1c08-40a5-851d-c272b3f1c248.json")),
+ ],
+ )
def test_tool_call_error(
self,
- client: Anthropic,
- respx_mock: MockRouter,
- monkeypatch: pytest.MonkeyPatch,
+ snapshot_client: Anthropic,
caplog: pytest.LogCaptureFixture,
) -> None:
called = None
@@ -136,7 +115,7 @@ def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResu
def tool_runner(client: Anthropic) -> List[Union[BetaMessageParam, None]]:
runner = client.beta.messages.tool_runner(
max_tokens=1024,
- model="claude-haiku-4-5",
+ model="claude-3-5-haiku-latest",
tools=[get_weather],
messages=[{"role": "user", "content": "What is the weather in SF?"}],
)
@@ -150,14 +129,7 @@ def tool_runner(client: Anthropic) -> List[Union[BetaMessageParam, None]]:
return actual_responses
with caplog.at_level(logging.ERROR):
- message = make_snapshot_request(
- tool_runner,
- content_snapshot=snapshots["tool_call_error"]["responses"],
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
- )
-
+ message = tool_runner(snapshot_client)
assert caplog.record_tuples == [
(
"anthropic.lib.tools._beta_runner",
@@ -165,14 +137,49 @@ def tool_runner(client: Anthropic) -> List[Union[BetaMessageParam, None]]:
"Error occurred while calling tool: get_weather",
),
]
- assert print_obj(message, monkeypatch) == snapshot(
- "[{'role': 'user', 'content': [{'type': 'tool_result', 'tool_use_id': 'toolu_01Do4cDVNxt51EuosKoxdmii', 'content': \"RuntimeError('Unexpected error, try again')\", 'is_error': True}]}]\n"
+ assert print_obj(message) == snapshot(
+ """\
+[
+ {
+ 'role': 'user',
+ 'content': [
+ {
+ 'type': 'tool_result',
+ 'tool_use_id': 'toolu_01ASSujsMzFEe4PjhTGzbmdi',
+ 'content': "RuntimeError('Unexpected error, try again')",
+ 'is_error': True
+ }
+ ]
+ },
+ {
+ 'role': 'user',
+ 'content': [
+ {
+ 'type': 'tool_result',
+ 'tool_use_id': 'toolu_01RPpGTPHZBTK27CruspxqvL',
+ 'content': '{"location": "San Francisco, CA", "temperature": "68\\\\u00b0F", "condition": "Sunny"}'
+ }
+ ]
+ },
+ {
+ 'role': 'user',
+ 'content': [
+ {
+ 'type': 'tool_result',
+ 'tool_use_id': 'toolu_014Qf7b4odsWZYj4ngf41v5R',
+ 'content': '{"location": "San Francisco, CA", "temperature": "20\\\\u00b0C", "condition": "Sunny"}'
+ }
+ ]
+ }
+]
+"""
)
- @pytest.mark.respx(base_url=base_url)
- def test_custom_message_handling(
- self, client: Anthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
- ) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:f1c3350a-cdb1-4508-a208-18544b825a9e.json"))],
+ )
+ def test_custom_message_handling(self, snapshot_client: Anthropic) -> None:
@beta_tool
def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType:
"""Lookup the weather for a given city in either celsius or fahrenheit
@@ -197,8 +204,9 @@ def custom_message_handling(client: Anthropic) -> BetaMessage:
# handle only where there is a tool call
if message.content[0].type == "tool_use":
runner.append_messages(
+ message,
BetaMessageParam(
- role="assistant",
+ role="user",
content=[
BetaToolResultBlockParam(
tool_use_id=message.content[0].id,
@@ -211,18 +219,47 @@ def custom_message_handling(client: Anthropic) -> BetaMessage:
return runner.until_done()
- message = make_snapshot_request(
- custom_message_handling,
- content_snapshot=snapshots["custom"]["responses"],
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
+ message = custom_message_handling(snapshot_client)
+ assert print_obj(message) == snapshot(
+ """\
+ParsedBetaMessage(
+ container=None,
+ content=[
+ ParsedBetaTextBlock(
+ citations=None,
+ parsed_output=None,
+ text='The weather in San Francisco, CA is currently **sunny** with a temperature of **20°C** (about 68°F).',
+ type='text'
+ )
+ ],
+ context_management=None,
+ id='msg_01Mp3aR278QuTCzCTtZNHAxD',
+ model='claude-haiku-4-5-20251001',
+ role='assistant',
+ stop_reason='end_turn',
+ stop_sequence=None,
+ type='message',
+ usage=BetaUsage(
+ cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0),
+ cache_creation_input_tokens=0,
+ cache_read_input_tokens=0,
+ input_tokens=763,
+ output_tokens=31,
+ server_tool_use=None,
+ service_tier='standard'
+ )
+)
+"""
)
- assert print_obj(message, monkeypatch) == snapshots["custom"]["result"]
-
- @pytest.mark.respx(base_url=base_url)
- def test_tool_call_caching(self, client: Anthropic, respx_mock: MockRouter) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:6401445a-5758-4777-9778-3008bae873ec.json"))],
+ )
+ def test_tool_call_caching(
+ self,
+ snapshot_client: Anthropic,
+ ) -> None:
called = None
@beta_tool
@@ -256,18 +293,13 @@ def tool_runner(client: Anthropic) -> None:
if response1 is not None:
assert response1 is response2
- make_snapshot_request(
- tool_runner,
- content_snapshot=snapshots["tool_call"]["responses"],
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
- )
+ tool_runner(snapshot_client)
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_call_sync(
- self, client: Anthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
- ) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:ff848716-2309-477f-8e90-39877809aaad.json"))],
+ )
+ def test_streaming_call_sync(self, snapshot_client: Anthropic) -> None:
@beta_tool
def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType:
"""Lookup the weather for a given city in either celsius or fahrenheit
@@ -280,24 +312,55 @@ def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResu
"""
return json.dumps(_get_weather(location, units))
- last_response_messsage = make_stream_snapshot_request(
- lambda c: c.beta.messages.tool_runner(
+ last_response_messsage = (
+ snapshot_client.beta.messages.tool_runner(
max_tokens=1024,
model="claude-haiku-4-5",
tools=[get_weather],
messages=[{"role": "user", "content": "What is the weather in SF?"}],
stream=True,
).until_done(),
- content_snapshot=external("hash:cd8d3d185e7a*.json"),
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
)
- assert print_obj(last_response_messsage, monkeypatch) == snapshots["streaming"]["result"]
+ assert print_obj(last_response_messsage) == snapshot(
+ """\
+(
+ ParsedBetaMessage(
+ container=None,
+ content=[
+ ParsedBetaTextBlock(
+ citations=None,
+ parsed_output=None,
+ text='The weather in San Francisco, CA is currently **Sunny** with a temperature of **68°F**.',
+ type='text'
+ )
+ ],
+ context_management=None,
+ id='msg_0112kkkttNuK7GZWAFeFrBWm',
+ model='claude-haiku-4-5-20251001',
+ role='assistant',
+ stop_reason='end_turn',
+ stop_sequence=None,
+ type='message',
+ usage=BetaUsage(
+ cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0),
+ cache_creation_input_tokens=0,
+ cache_read_input_tokens=0,
+ input_tokens=770,
+ output_tokens=25,
+ server_tool_use=None,
+ service_tier='standard'
+ )
+ ),
+)
+"""
+ )
- @pytest.mark.respx(base_url=base_url)
- def test_max_iterations(self, client: Anthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:4a71c9f9-6191-4820-b1a1-89f3bb179078.json"))],
+ )
+ def test_max_iterations(self, snapshot_client: Anthropic) -> None:
@beta_tool
def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType:
"""Lookup the weather for a given city in either celsius or fahrenheit
@@ -335,25 +398,43 @@ def get_weather_answers(client: Anthropic) -> List[Union[BetaMessageParam, None]
return answers
- answers = make_snapshot_request(
- get_weather_answers,
- content_snapshot=snapshot(
- [
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_017GvdrboNn8hipoMJUcK8m6", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "I\'ll get the weather for each of these cities one at a time. Let me start with San Francisco."}, {"type": "tool_use", "id": "toolu_011Q6hjHnpWegJvV1Zn6Cm1h", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "f"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 701, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 96, "service_tier": "standard"}}',
- '{"model": "claude-haiku-4-5-20251001", "id": "msg_01PYFQH4AkK3NBgSpFkWD16q", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "Now let me check New York."}, {"type": "tool_use", "id": "toolu_011QaaAuMeNWTwHjkxcxce1D", "name": "get_weather", "input": {"location": "New York, NY", "units": "f"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 837, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 81, "service_tier": "standard"}}',
- ]
- ),
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
- )
+ answers = get_weather_answers(snapshot_client)
- assert print_obj(answers, monkeypatch) == snapshot(
- "[{'role': 'user', 'content': [{'type': 'tool_result', 'tool_use_id': 'toolu_011Q6hjHnpWegJvV1Zn6Cm1h', 'content': '{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\\\u00b0F\", \"condition\": \"Sunny\"}'}]}, {'role': 'user', 'content': [{'type': 'tool_result', 'tool_use_id': 'toolu_011QaaAuMeNWTwHjkxcxce1D', 'content': '{\"location\": \"New York, NY\", \"temperature\": \"68\\\\u00b0F\", \"condition\": \"Sunny\"}'}]}]\n"
+ assert print_obj(answers) == snapshot(
+ """\
+[
+ {
+ 'role': 'user',
+ 'content': [
+ {
+ 'type': 'tool_result',
+ 'tool_use_id': 'toolu_01DUC5sKZofEcuFX4NTvXAGp',
+ 'content': '{"location": "San Francisco, CA", "temperature": "68\\\\u00b0F", "condition": "Sunny"}'
+ }
+ ]
+ },
+ {
+ 'role': 'user',
+ 'content': [
+ {
+ 'type': 'tool_result',
+ 'tool_use_id': 'toolu_01B3V3NpBkEHenKXsgAW7mhQ',
+ 'content': '{"location": "New York, NY", "temperature": "68\\\\u00b0F", "condition": "Sunny"}'
+ }
+ ]
+ }
+]
+"""
)
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_call_sync_events(self, client: Anthropic, respx_mock: MockRouter) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:f27add0c-5771-4452-8ed9-996f5d74ef77.json"))],
+ )
+ def test_streaming_call_sync_events(
+ self,
+ snapshot_client: Anthropic,
+ ) -> None:
@beta_tool
def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType:
"""Lookup the weather for a given city in either celsius or fahrenheit
@@ -381,13 +462,7 @@ def accumulate_events(client: Anthropic) -> List[str]:
events.append(event.type)
return events
- events = make_stream_snapshot_request(
- accumulate_events,
- content_snapshot=external("uuid:9cb114c8-69bd-4111-841b-edee30333afd.json"),
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
- )
+ events = accumulate_events(snapshot_client)
assert set(events) == snapshot(
{
"content_block_delta",
@@ -401,10 +476,11 @@ def accumulate_events(client: Anthropic) -> List[str]:
}
)
- @pytest.mark.respx(base_url=base_url)
- def test_compaction_control(
- self, client: Anthropic, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture
- ) -> None:
+ @pytest.mark.parametrize(
+ "http_snapshot",
+ [cast(Any, external("uuid:e4c374b8-ae08-48f3-96e5-9d28066820b0.json"))],
+ )
+ def test_compaction_control(self, snapshot_client: Anthropic, caplog: pytest.LogCaptureFixture) -> None:
@beta_tool
def submit_analysis(summary: str) -> str: # noqa: ARG001
"""Call this LAST with your final analysis."""
@@ -435,13 +511,7 @@ def tool_runner(client: Anthropic) -> BetaToolRunner[None]:
return runner
with caplog.at_level(logging.INFO, logger="anthropic.lib.tools._beta_runner"):
- runner = make_snapshot_request(
- tool_runner,
- content_snapshot=external("uuid:ab7b2edd-9c2d-4f53-9c04-92bb659b9caa.json"),
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
- )
+ runner = tool_runner(snapshot_client)
messages = list(runner._params["messages"])
assert len(messages) == 1
@@ -453,50 +523,46 @@ def tool_runner(client: Anthropic) -> BetaToolRunner[None]:
assert content["text"] == snapshot("""\
## 1. Task Overview
-The user requests a 500-word essay about dogs, cats, and birds, followed by a single call to the `submit_analysis` tool at the end containing information about all three animals. \n\
-
-**Key constraints:**
-- Essay must be detailed and approximately 500 words
-- Must cover all three animals: dogs, cats, and birds
-- Tool `submit_analysis` must be called exactly once, at the end
-- Tool call should contain information about all three animals
+The user requested a detailed 500-word essay about dogs, cats, and birds, followed by a single call to a tool named `submit_analysis` with information about all three animals. Key requirements:
+- Essay must be approximately 500 words
+- Must cover all three animal types: dogs, cats, and birds
+- Must call `submit_analysis` tool exactly once at the end
+- The tool call should contain information about all three animals
## 2. Current State
-**Completed:** Nothing has been completed yet.
-
-**Status:** The task has been acknowledged but no essay has been written and no tool has been called.
+**Completed:** Nothing has been completed yet. This appears to be the initial state of the task.
-**Artifacts produced:** None yet.
+**Not yet done:**
+- The 500-word essay has not been written
+- The `submit_analysis` tool has not been called
## 3. Important Discoveries
-**Technical requirements:**
-- Need to understand the parameters/schema for `submit_analysis` tool (not yet verified)
-- Must structure the tool call to include data about all three animal types in a single invocation
+**Constraints identified:**
+- The tool `submit_analysis` must be called only once, not separately for each animal
+- The tool call should occur after the essay is complete
+- Need to determine the proper parameters/format for `submit_analysis` tool (this information was not provided in the initial request)
-**Approach to take:**
-- Write a comprehensive 500-word essay discussing dogs, cats, and birds
-- Essay should cover characteristics, behaviors, and comparisons between the three
-- Extract/organize key information about each animal for the tool call
-- Call `submit_analysis` once with consolidated data about all three animals
+**Uncertainty:**
+- The exact structure and parameters expected by `submit_analysis` are unclear. Will need to infer or use reasonable structure when calling the tool (e.g., might expect JSON with animal names and their characteristics, or separate parameters for each animal)
## 4. Next Steps
1. **Write the 500-word essay** covering:
- - Dogs: characteristics, behavior, relationship with humans
- - Cats: characteristics, behavior, relationship with humans
- - Birds: characteristics, behavior, diversity
- - Comparisons and contrasts between the three
- \n\
-2. **Determine the schema for `submit_analysis` tool** - check what parameters it accepts and how to structure data about multiple animals
-
-3. **Call `submit_analysis` once** with information about all three animals in the appropriate format
+ - Dogs (characteristics, behavior, relationship with humans)
+ - Cats (characteristics, behavior, relationship with humans)
+ - Birds (characteristics, behavior, varieties/examples)
+ - Make it detailed and well-structured
-4. **Verify word count** is approximately 500 words
+2. **Call submit_analysis tool once** after the essay with comprehensive information about all three animals. Since the tool structure wasn't specified, use a logical format such as:
+ - Pass information about dogs, cats, and birds as parameters
+ - Could structure as JSON or separate arguments depending on tool requirements
+ - Include key facts and characteristics for each animal type
## 5. Context to Preserve
-- User emphasized calling the tool "only once at the end"
-- Essay should be "detailed" - not superficial
-- The tool call must encompass information about all three animals, not separate calls per animal
-- This appears to be a test of following multi-step instructions precisely
+- User specifically emphasized calling the tool "only once at the end"
+- Essay should be "detailed" - not just superficial information
+- Word count target is approximately 500 words
+- All three animals must be covered adequately in the essay
+- The tool call is a critical component - not optional
\
""")
assert caplog.record_tuples == snapshot(
@@ -504,54 +570,46 @@ def tool_runner(client: Anthropic) -> BetaToolRunner[None]:
(
"anthropic.lib.tools._beta_runner",
20,
- "Token usage 1615 has exceeded the threshold of 500. Performing compaction.",
+ "Token usage 1609 has exceeded the threshold of 500. Performing compaction.",
),
- ("anthropic.lib.tools._beta_runner", 20, "Compaction complete. New token usage: 496"),
+ ("anthropic.lib.tools._beta_runner", 20, "Compaction complete. New token usage: 525"),
]
)
- @pytest.mark.parametrize("client", [False], indirect=True)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.parametrize("http_snapshot", [cast(Any, external("uuid:6f763c87-ecb6-4217-8f6f-32db8e84a6be.json"))])
def test_server_side_tool(
self,
- client: Anthropic,
- respx_mock: MockRouter,
+ snapshot_client: Anthropic,
) -> None:
- def tool_runner(client: Anthropic) -> BetaToolRunner[None]:
- runner = client.beta.messages.tool_runner(
- model="claude-haiku-4-5",
- messages=[{"role": "user", "content": "What is the weather in SF?"}],
- tools=[
- {
- "type": "web_search_20250305",
- "name": "web_search",
- }
- ],
- max_tokens=1024,
- )
-
- message = next(runner)
-
- content_types = [content.type for content in message.content]
+ runner = snapshot_client.beta.messages.tool_runner(
+ model="claude-haiku-4-5",
+ messages=[{"role": "user", "content": "What is the weather in SF?"}],
+ tools=[
+ {
+ "type": "web_search_20250305",
+ "name": "web_search",
+ }
+ ],
+ max_tokens=1024,
+ )
- assert "server_tool_use" in content_types
- assert "web_search_tool_result" in content_types
+ message = next(runner)
- return runner
+ content_types = [content.type for content in message.content]
- make_snapshot_request(
- tool_runner,
- content_snapshot=external("uuid:a0a711eb-ee0e-4a42-88d6-5c7f83c0f25a.txt"),
- path="/v1/messages",
- mock_client=client,
- respx_mock=respx_mock,
- )
+ assert "server_tool_use" in content_types
+ assert "web_search_tool_result" in content_types
@pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1")
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.parametrize(
+ "http_snapshot",
+ [
+ cast(Any, external("uuid:c4061e82-ea59-4756-9549-71a35da24499.json")),
+ ],
+)
async def test_basic_call_async(
- async_client: AsyncAnthropic, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+ async_snapshot_client: AsyncAnthropic,
) -> None:
@beta_async_tool
async def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType:
@@ -565,20 +623,12 @@ async def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionTo
"""
return json.dumps(_get_weather(location, units))
- message = await make_async_snapshot_request(
- lambda c: c.beta.messages.tool_runner(
- max_tokens=1024,
- model="claude-3-7",
- tools=[get_weather],
- messages=[{"role": "user", "content": "What is the weather in SF?"}],
- ).until_done(),
- content_snapshot=snapshots["basic"]["responses"],
- path="/v1/messages",
- mock_client=async_client,
- respx_mock=respx_mock,
- )
-
- assert print_obj(message, monkeypatch) == snapshots["basic"]["result"]
+ await async_snapshot_client.beta.messages.tool_runner(
+ max_tokens=1024,
+ model="claude-haiku-4-5",
+ tools=[get_weather],
+ messages=[{"role": "user", "content": "What is the weather in SF?"}],
+ ).until_done()
def _get_weather(location: str, units: Literal["c", "f"]) -> Dict[str, Any]:
diff --git a/tests/lib/utils.py b/tests/lib/utils.py
index d6093dd3..4b61198d 100644
--- a/tests/lib/utils.py
+++ b/tests/lib/utils.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import io
+import re
import inspect
from typing import Any, Iterable
from typing_extensions import TypeAlias
@@ -8,11 +9,13 @@
import rich
import pytest
import pydantic
+import rich.pretty
+import rich.console
ReprArgs: TypeAlias = "Iterable[tuple[str | None, Any]]"
-def print_obj(obj: object, monkeypatch: pytest.MonkeyPatch) -> str:
+def print_obj(obj: object) -> str:
"""Pretty print an object to a string"""
# monkeypatch pydantic model printing so that model fields
@@ -28,7 +31,7 @@ def __repr_name__(self: pydantic.BaseModel) -> str:
# e.g. `GenericModel[Location]` -> `GenericModel`
return self.__class__.__name__.split("[", maxsplit=1)[0]
- with monkeypatch.context() as m:
+ with pytest.MonkeyPatch.context() as m:
m.setattr(pydantic.BaseModel, "__repr_args__", __repr_args__)
m.setattr(pydantic.BaseModel, "__repr_name__", __repr_name__)
@@ -62,9 +65,22 @@ def clear_locals(string: str, *, stacklevel: int) -> str:
def rich_print_str(obj: object) -> str:
"""Like `rich.print()` but returns the string instead"""
- buf = io.StringIO()
-
- console = rich.console.Console(file=buf, width=120)
- console.out(obj)
- return buf.getvalue()
+ buf = io.StringIO()
+ console = rich.console.Console(
+ file=buf,
+ width=120,
+ force_terminal=False,
+ color_system=None,
+ record=True,
+ )
+
+ # Use Rich's pretty printer for nice multi-line formatting
+ rich.pretty.install(console)
+ console.print(obj, overflow="fold", width=120)
+
+ result = buf.getvalue()
+ # Strip out [~...] patterns to exclude generic content from snapshots
+ result = re.sub(r"\[~[^\]]*\]", "", result)
+ result = result.replace("[TypeVar]", "")
+ return result
diff --git a/uv.lock b/uv.lock
index f2064b8c..f0774156 100644
--- a/uv.lock
+++ b/uv.lock
@@ -216,8 +216,8 @@ vertex = [
dev = [
{ name = "boto3-stubs" },
{ name = "dirty-equals" },
- { name = "griffe", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" },
- { name = "griffe", version = "1.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" },
+ { name = "griffe" },
+ { name = "http-snapshot", extra = ["httpx"] },
{ name = "importlib-metadata" },
{ name = "inline-snapshot" },
{ name = "mypy" },
@@ -263,6 +263,7 @@ dev = [
{ name = "boto3-stubs", specifier = ">=1" },
{ name = "dirty-equals", specifier = ">=0.6.0" },
{ name = "griffe", specifier = ">=1" },
+ { name = "http-snapshot", extras = ["httpx"], specifier = "==0.1.6" },
{ name = "importlib-metadata", specifier = ">=6.7.0" },
{ name = "inline-snapshot", specifier = ">=0.28.0" },
{ name = "mypy", specifier = "==1.17" },
@@ -733,44 +734,40 @@ requests = [
[[package]]
name = "griffe"
-version = "1.14.0"
+version = "1.13.0"
source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
dependencies = [
- { name = "colorama", marker = "python_full_version < '3.10' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" },
+ { name = "colorama" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c6/b5/23b91f22b7b3a7f8f62223f6664946271c0f5cb4179605a3e6bbae863920/griffe-1.13.0.tar.gz", hash = "sha256:246ea436a5e78f7fbf5f24ca8a727bb4d2a4b442a2959052eea3d0bfe9a076e0", size = 412759, upload-time = "2025-08-26T13:27:11.422Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/8c/b7cfdd8dfe48f6b09f7353323732e1a290c388bd14f216947928dc85f904/griffe-1.13.0-py3-none-any.whl", hash = "sha256:470fde5b735625ac0a36296cd194617f039e9e83e301fcbd493e2b58382d0559", size = 139365, upload-time = "2025-08-26T13:27:09.882Z" },
]
[[package]]
-name = "griffe"
-version = "1.15.0"
+name = "h11"
+version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'",
- "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'",
- "python_full_version >= '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'",
- "python_full_version >= '3.10' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version >= '3.10' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/0d/0c/3a471b6e31951dce2360477420d0a8d1e00dea6cf33b70f3e8c3ab6e28e1/griffe-1.15.0.tar.gz", hash = "sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea", size = 424112, upload-time = "2025-11-10T15:03:15.52Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705, upload-time = "2025-11-10T15:03:13.549Z" },
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
-name = "h11"
-version = "0.16.0"
+name = "http-snapshot"
+version = "0.1.6"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+dependencies = [
+ { name = "inline-snapshot" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/41/a7/c94382d2b1bad33f9e7ac34048e0784be78f21c31693e8b354eec72a261a/http_snapshot-0.1.6.tar.gz", hash = "sha256:16b1cc04645e84b5d225ba272b3df4a29bbdb84386087e8435806299a7895a5d", size = 10835, upload-time = "2025-12-16T10:12:03.162Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/43/749861077584bd35c73482d38f625cd349efc6a5ec6f03ab8768ed5a6432/http_snapshot-0.1.6-py3-none-any.whl", hash = "sha256:a79adc33f840643750a5ecbd55988ebc009097d962af71857924aafcc16c086a", size = 11626, upload-time = "2025-12-16T10:12:01.823Z" },
+]
+
+[package.optional-dependencies]
+httpx = [
+ { name = "httpx" },
]
[[package]]