diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2969a756f..820b5c1ae 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.111.0" + ".": "0.112.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 09cef8716..d511ee272 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 116 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic/anthropic-2ecf157aa324d82fa8f3636a325aea5bd96ecab93193f6e37864ebe665c48685.yml -openapi_spec_hash: 29873ea69a87e047c4f742a960648bf0 -config_hash: 48f7cbc6648bf7f1e6c68ad3dab477fc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic/anthropic-106b972584d8eb2dc0b0319a2cfc0a2528544e35083d3264b82f8e22ae3300b8.yml +openapi_spec_hash: 79b933da300d4134d1964d83f33afb0c +config_hash: ce2cd5d2f03228adacf04ebcceb14465 diff --git a/CHANGELOG.md b/CHANGELOG.md index 228c6ac2d..e7f05913e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 0.112.0 (2026-06-23) + +Full Changelog: [v0.111.0...v0.112.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.111.0...v0.112.0) + +### Features + +* **client:** add support for system.message streaming events ([2450d59](https://github.com/anthropics/anthropic-sdk-python/commit/2450d595731f9532080bb94eb8a43c0bd5189659)) + + +### Bug Fixes + +* **memory tool:** create parent directories with the correct permissions ([#135](https://github.com/anthropics/anthropic-sdk-python/issues/135)) ([f2fc2a9](https://github.com/anthropics/anthropic-sdk-python/commit/f2fc2a9e0ad8507e4108e9a6b85d023416c2f14c)) + + +### Chores + +* **api:** add support for sending User Profile ID in request headers ([83319be](https://github.com/anthropics/anthropic-sdk-python/commit/83319bed74f4414d54e0f4237d70b945ed671008)) + ## 0.111.0 (2026-06-18) Full Changelog: [v0.110.0...v0.111.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.110.0...v0.111.0) diff --git a/pyproject.toml b/pyproject.toml index 4f9145008..59777aabc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "anthropic" -version = "0.111.0" +version = "0.112.0" description = "The official Python library for the anthropic API" dynamic = ["readme"] license = "MIT" diff --git a/src/anthropic/_streaming.py b/src/anthropic/_streaming.py index 8b301cf9d..2ac075c52 100644 --- a/src/anthropic/_streaming.py +++ b/src/anthropic/_streaming.py @@ -138,6 +138,7 @@ def __stream__(self) -> Iterator[_T]: or sse.event == "session.thread_status_idle" or sse.event == "session.thread_status_rescheduled" or sse.event == "session.thread_status_terminated" + or sse.event == "system.message" ): data = sse.json() if is_dict(data) and "type" not in data: @@ -304,6 +305,7 @@ async def __stream__(self) -> AsyncIterator[_T]: or sse.event == "session.thread_status_idle" or sse.event == "session.thread_status_rescheduled" or sse.event == "session.thread_status_terminated" + or sse.event == "system.message" ): data = sse.json() if is_dict(data) and "type" not in data: diff --git a/src/anthropic/_version.py b/src/anthropic/_version.py index 2e20b371d..30c9267dd 100644 --- a/src/anthropic/_version.py +++ b/src/anthropic/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "anthropic" -__version__ = "0.111.0" # x-release-please-version +__version__ = "0.112.0" # x-release-please-version diff --git a/src/anthropic/lib/tools/_beta_builtin_memory_tool.py b/src/anthropic/lib/tools/_beta_builtin_memory_tool.py index edb948a26..b995fbb3d 100644 --- a/src/anthropic/lib/tools/_beta_builtin_memory_tool.py +++ b/src/anthropic/lib/tools/_beta_builtin_memory_tool.py @@ -303,6 +303,37 @@ def _atomic_write_file(target_path: Path, content: str) -> None: raise +def _secure_mkdir(path: Path, mode: int = _DIR_CREATE_MODE) -> None: + """Create ``path`` and any missing parents with ``mode``, regardless of umask. + + ``Path.mkdir(parents=True, mode=...)`` and ``os.makedirs(mode=...)`` apply the + requested mode only to the final (leaf) directory; intermediate parents are + created with the process umask default, which can be world-writable under a + permissive umask. We create each missing component explicitly so the entire + newly-created chain has restrictive permissions — closing a symlink-swap hole + where an attacker with write access to a world-writable parent could replace + the sandbox root and defeat path validation. + """ + missing: list[Path] = [] + current = path + while not current.exists(): + missing.append(current) + parent = current.parent + if parent == current: # reached filesystem root + break + current = parent + for directory in reversed(missing): + try: + directory.mkdir(mode=mode) + except FileExistsError: + # Created concurrently between our exists() check and mkdir(); skip. + continue + # mkdir() is subject to umask; chmod is not. Enforce the exact mode so a + # restrictive umask can't strip owner bits. (We only chmod dirs we just + # created and therefore own — never pre-existing dirs.) + os.chmod(directory, mode) + + def _validate_no_symlink_escape(target_path: Path, memory_root: Path) -> None: resolved_root = memory_root.resolve() @@ -351,7 +382,7 @@ def __init__(self, base_path: str = "./memory"): super().__init__() self.base_path = Path(base_path) self.memory_root = self.base_path / "memories" - self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + _secure_mkdir(self.memory_root) def _validate_path(self, path: str) -> Path: """Validate and resolve memory paths""" @@ -443,7 +474,7 @@ def collect_items(dir_path: Path, relative_path: str, depth: int) -> None: def create(self, command: BetaMemoryTool20250818CreateCommand) -> str: full_path = self._validate_path(command.path) - full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + _secure_mkdir(full_path.parent) try: fd = os.open(full_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) @@ -558,7 +589,7 @@ def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str: if new_full_path.exists(): raise ToolError(f"The destination {command.new_path} already exists") - new_full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + _secure_mkdir(new_full_path.parent) try: old_full_path.rename(new_full_path) @@ -572,7 +603,7 @@ def clear_all_memory(self) -> str: """Override the base implementation to provide file system clearing.""" if self.memory_root.exists(): shutil.rmtree(self.memory_root) - self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + _secure_mkdir(self.memory_root) return "All memory cleared" @@ -613,6 +644,10 @@ async def _async_validate_no_symlink_escape(target_path: AsyncPath, memory_root: await run_sync(_validate_no_symlink_escape, sync_target, sync_root) +async def _async_secure_mkdir(path: AsyncPath, mode: int = _DIR_CREATE_MODE) -> None: + await run_sync(_secure_mkdir, Path(str(path)), mode) + + async def _async_read_file_content(full_path: AsyncPath, memory_path: str) -> str: try: return await full_path.read_text(encoding="utf-8") @@ -633,7 +668,7 @@ def __init__(self, base_path: str = "./memory"): async def _ensure_memory_root(self) -> None: """Ensure the memory root directory exists""" - await self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + await _async_secure_mkdir(self.memory_root) async def _validate_path(self, path: str) -> AsyncPath: """Validate and resolve memory paths""" @@ -733,7 +768,7 @@ async def create(self, command: BetaMemoryTool20250818CreateCommand) -> str: await self._ensure_memory_root() full_path = await self._validate_path(command.path) - await full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + await _async_secure_mkdir(full_path.parent) try: sync_full_path = Path(str(full_path)) @@ -857,7 +892,7 @@ async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str: if await new_full_path.exists(): raise ToolError(f"The destination {command.new_path} already exists") - await new_full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + await _async_secure_mkdir(new_full_path.parent) try: await old_full_path.rename(new_full_path) @@ -871,5 +906,5 @@ async def clear_all_memory(self) -> str: """Override the base implementation to provide file system clearing.""" if await self.memory_root.exists(): await run_sync(shutil.rmtree, str(self.memory_root)) - await self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) + await _async_secure_mkdir(self.memory_root) return "All memory cleared" diff --git a/src/anthropic/lib/tools/_beta_session_runner.py b/src/anthropic/lib/tools/_beta_session_runner.py index 35dc51926..96d943f5c 100644 --- a/src/anthropic/lib/tools/_beta_session_runner.py +++ b/src/anthropic/lib/tools/_beta_session_runner.py @@ -218,9 +218,7 @@ def _scoped_client(client: AsyncAnthropic, environment_key: str | None) -> Async mutated). """ if environment_key is not None: - return _copy_client_with_bearer_auth( - client, auth_token=environment_key, helper="session-tool-runner" - ) + return _copy_client_with_bearer_auth(client, auth_token=environment_key, helper="session-tool-runner") return client.with_options(default_headers=helper_header("session-tool-runner")) diff --git a/src/anthropic/resources/beta/agents/agents.py b/src/anthropic/resources/beta/agents/agents.py index 5a28dc982..b72fe71ce 100644 --- a/src/anthropic/resources/beta/agents/agents.py +++ b/src/anthropic/resources/beta/agents/agents.py @@ -99,7 +99,9 @@ def create( description: Description of what the agent does. mcp_servers: MCP servers this agent connects to. Maximum 20. Names must be unique within the - array. + array. Every server must be referenced by an `mcp_toolset` in `tools`; + unreferenced servers are rejected. See the + [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. @@ -245,8 +247,11 @@ def update( description: Description. Omit to preserve; send empty string or null to clear. - mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or null to - clear. Names must be unique. Maximum 20. + mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or `null` to + clear. Names must be unique. Maximum 20. Every server must be referenced by an + `mcp_toolset` in the agent's resulting `tools`; unreferenced servers are + rejected. See the + [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars @@ -496,7 +501,9 @@ async def create( description: Description of what the agent does. mcp_servers: MCP servers this agent connects to. Maximum 20. Names must be unique within the - array. + array. Every server must be referenced by an `mcp_toolset` in `tools`; + unreferenced servers are rejected. See the + [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. @@ -642,8 +649,11 @@ async def update( description: Description. Omit to preserve; send empty string or null to clear. - mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or null to - clear. Names must be unique. Maximum 20. + mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or `null` to + clear. Names must be unique. Maximum 20. Every server must be referenced by an + `mcp_toolset` in the agent's resulting `tools`; unreferenced servers are + rejected. See the + [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars diff --git a/src/anthropic/resources/beta/messages/batches.py b/src/anthropic/resources/beta/messages/batches.py index 5c2ad54da..bbf2b74ce 100644 --- a/src/anthropic/resources/beta/messages/batches.py +++ b/src/anthropic/resources/beta/messages/batches.py @@ -51,6 +51,7 @@ def create( *, requests: Iterable[batch_create_params.Request], betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -74,6 +75,11 @@ def create( betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting + on behalf of a party other than your organization. Requires the `user-profiles` + beta header. Applies to every request in the batch; an individual request whose + `user_profile_id` body field conflicts with this header is errored. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -87,7 +93,8 @@ def create( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) - else not_given + else not_given, + "anthropic-user-profile-id": user_profile_id, } ), **(extra_headers or {}), @@ -440,6 +447,7 @@ async def create( *, requests: Iterable[batch_create_params.Request], betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -463,6 +471,11 @@ async def create( betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting + on behalf of a party other than your organization. Requires the `user-profiles` + beta header. Applies to every request in the batch; an individual request whose + `user_profile_id` body field conflicts with this header is errored. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -476,7 +489,8 @@ async def create( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) - else not_given + else not_given, + "anthropic-user-profile-id": user_profile_id, } ), **(extra_headers or {}), diff --git a/src/anthropic/resources/beta/messages/messages.py b/src/anthropic/resources/beta/messages/messages.py index 57a35fb45..d37e60589 100644 --- a/src/anthropic/resources/beta/messages/messages.py +++ b/src/anthropic/resources/beta/messages/messages.py @@ -146,8 +146,8 @@ def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -443,11 +443,11 @@ def create( Recommended for advanced use cases only. - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -487,8 +487,8 @@ def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -784,11 +784,11 @@ def create( Recommended for advanced use cases only. - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -828,8 +828,8 @@ def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1125,11 +1125,11 @@ def create( Recommended for advanced use cases only. - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1169,8 +1169,8 @@ def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1204,7 +1204,12 @@ def create( merged_output_config = _merge_output_configs(output_config, output_format) extra_headers = merge_headers( - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -1237,7 +1242,6 @@ def create( "tools": tools, "top_k": top_k, "top_p": top_p, - "user_profile_id": user_profile_id, }, message_create_params.MessageCreateParamsStreaming if stream @@ -1279,8 +1283,8 @@ def parse( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1318,7 +1322,12 @@ def parse( extra_headers = merge_headers( _helper_header("beta.messages.parse"), - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -1381,7 +1390,6 @@ def parser(response: BetaMessage) -> ParsedBetaMessage[ResponseFormatT]: "tools": tools, "top_k": top_k, "top_p": top_p, - "user_profile_id": user_profile_id, }, message_create_params.MessageCreateParamsNonStreaming, ), @@ -1427,8 +1435,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1468,8 +1476,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1509,8 +1517,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1549,8 +1557,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1585,7 +1593,12 @@ def tool_runner( extra_headers = merge_headers( _helper_header("BetaToolRunner"), - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -1626,7 +1639,6 @@ def tool_runner( "tools": [*[tool.to_dict() for tool in runnable_tools], *raw_tools], "top_k": top_k, "top_p": top_p, - "user_profile_id": user_profile_id, }, ) @@ -1685,8 +1697,8 @@ def stream( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1717,7 +1729,12 @@ def stream( _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "beta.messages", }, - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -1773,7 +1790,6 @@ def stream( "top_p": top_p, "tools": tools, "tool_choice": tool_choice, - "user_profile_id": user_profile_id, "stream": True, }, message_create_params.MessageCreateParams, @@ -2115,8 +2131,8 @@ async def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2412,11 +2428,11 @@ async def create( Recommended for advanced use cases only. - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2456,8 +2472,8 @@ async def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2753,11 +2769,11 @@ async def create( Recommended for advanced use cases only. - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2797,8 +2813,8 @@ async def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3094,11 +3110,11 @@ async def create( Recommended for advanced use cases only. - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - betas: Optional header to specify the beta version(s) you want to use. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -3138,8 +3154,8 @@ async def create( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3173,7 +3189,12 @@ async def create( merged_output_config = _merge_output_configs(output_config, output_format) extra_headers = merge_headers( - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -3206,7 +3227,6 @@ async def create( "tools": tools, "top_k": top_k, "top_p": top_p, - "user_profile_id": user_profile_id, }, message_create_params.MessageCreateParamsStreaming if stream @@ -3248,8 +3268,8 @@ async def parse( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3286,7 +3306,12 @@ async def parse( extra_headers = merge_headers( _helper_header("beta.messages.parse"), - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -3349,7 +3374,6 @@ def parser(response: BetaMessage) -> ParsedBetaMessage[ResponseFormatT]: "tools": tools, "top_k": top_k, "top_p": top_p, - "user_profile_id": user_profile_id, }, message_create_params.MessageCreateParamsNonStreaming, ), @@ -3395,8 +3419,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3436,8 +3460,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3477,8 +3501,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3517,8 +3541,8 @@ def tool_runner( top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3546,7 +3570,12 @@ def tool_runner( extra_headers = merge_headers( _helper_header("BetaToolRunner"), - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -3587,7 +3616,6 @@ def tool_runner( "tools": [*[tool.to_dict() for tool in runnable_tools], *raw_tools], "top_k": top_k, "top_p": top_p, - "user_profile_id": user_profile_id, }, ) @@ -3646,8 +3674,8 @@ def stream( tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3677,7 +3705,12 @@ def stream( _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "beta.messages", }, - strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), + strip_not_given( + { + "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, + "anthropic-user-profile-id": user_profile_id, + } + ), _stainless_helper_header(tools, messages), extra_headers or {}, ) @@ -3732,7 +3765,6 @@ def stream( "top_p": top_p, "tools": tools, "tool_choice": tool_choice, - "user_profile_id": user_profile_id, "stream": True, }, message_create_params.MessageCreateParams, diff --git a/src/anthropic/resources/messages/batches.py b/src/anthropic/resources/messages/batches.py index 213337119..009537347 100644 --- a/src/anthropic/resources/messages/batches.py +++ b/src/anthropic/resources/messages/batches.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -48,6 +48,7 @@ def create( self, *, requests: Iterable[batch_create_params.Request], + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -69,6 +70,11 @@ def create( requests: List of requests for prompt completion. Each is an individual request to create a Message. + user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting + on behalf of a party other than your organization. Requires the `user-profiles` + beta header. Applies to every request in the batch; an individual request whose + `user_profile_id` body field conflicts with this header is errored. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -77,6 +83,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return self._post( "/v1/messages/batches", body=maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), @@ -351,6 +358,7 @@ async def create( self, *, requests: Iterable[batch_create_params.Request], + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -372,6 +380,11 @@ async def create( requests: List of requests for prompt completion. Each is an individual request to create a Message. + user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting + on behalf of a party other than your organization. Requires the `user-profiles` + beta header. Applies to every request in the batch; an individual request whose + `user_profile_id` body field conflicts with this header is errored. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -380,6 +393,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return await self._post( "/v1/messages/batches", body=await async_maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), diff --git a/src/anthropic/resources/messages/messages.py b/src/anthropic/resources/messages/messages.py index 27e6e0014..89015d7d9 100644 --- a/src/anthropic/resources/messages/messages.py +++ b/src/anthropic/resources/messages/messages.py @@ -25,7 +25,7 @@ AsyncBatchesWithStreamingResponse, ) from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import is_given, required_args, maybe_transform, async_maybe_transform +from ..._utils import is_given, required_args, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._models import TypeAdapter from ..._resource import SyncAPIResource, AsyncAPIResource @@ -137,6 +137,7 @@ def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -389,6 +390,9 @@ def create( Recommended for advanced use cases only. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -421,6 +425,7 @@ def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -673,6 +678,9 @@ def create( Recommended for advanced use cases only. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -705,6 +713,7 @@ def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -957,6 +966,9 @@ def create( Recommended for advanced use cases only. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -989,6 +1001,7 @@ def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1015,6 +1028,7 @@ def create( stacklevel=3, ) + extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return self._post( "/v1/messages", body=maybe_transform( @@ -1071,6 +1085,7 @@ def stream( thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1094,6 +1109,7 @@ def stream( ) extra_headers = { + **strip_not_given({"anthropic-user-profile-id": user_profile_id}), _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "messages", **(extra_headers or {}), @@ -1591,6 +1607,7 @@ async def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1843,6 +1860,9 @@ async def create( Recommended for advanced use cases only. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1875,6 +1895,7 @@ async def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2127,6 +2148,9 @@ async def create( Recommended for advanced use cases only. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2159,6 +2183,7 @@ async def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2411,6 +2436,9 @@ async def create( Recommended for advanced use cases only. + user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a + party other than your organization. Requires the `user-profiles` beta header. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2443,6 +2471,7 @@ async def create( tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2469,6 +2498,7 @@ async def create( stacklevel=3, ) + extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return await self._post( "/v1/messages", body=await async_maybe_transform( @@ -2525,6 +2555,7 @@ def stream( thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, + user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2548,6 +2579,7 @@ def stream( ) extra_headers = { + **strip_not_given({"anthropic-user-profile-id": user_profile_id}), _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "messages", **(extra_headers or {}), diff --git a/src/anthropic/types/beta/agent_create_params.py b/src/anthropic/types/beta/agent_create_params.py index 698853258..176baba62 100644 --- a/src/anthropic/types/beta/agent_create_params.py +++ b/src/anthropic/types/beta/agent_create_params.py @@ -38,7 +38,10 @@ class AgentCreateParams(TypedDict, total=False): mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] """MCP servers this agent connects to. - Maximum 20. Names must be unique within the array. + Maximum 20. Names must be unique within the array. Every server must be + referenced by an `mcp_toolset` in `tools`; unreferenced servers are rejected. + See the + [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). """ metadata: Dict[str, str] diff --git a/src/anthropic/types/beta/agent_update_params.py b/src/anthropic/types/beta/agent_update_params.py index 1b0d79a34..8a9702a54 100644 --- a/src/anthropic/types/beta/agent_update_params.py +++ b/src/anthropic/types/beta/agent_update_params.py @@ -33,8 +33,10 @@ class AgentUpdateParams(TypedDict, total=False): mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] """MCP servers. - Full replacement. Omit to preserve; send empty array or null to clear. Names - must be unique. Maximum 20. + Full replacement. Omit to preserve; send empty array or `null` to clear. Names + must be unique. Maximum 20. Every server must be referenced by an `mcp_toolset` + in the agent's resulting `tools`; unreferenced servers are rejected. See the + [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). """ metadata: Optional[Dict[str, Optional[str]]] diff --git a/src/anthropic/types/beta/beta_cache_control_ephemeral_param.py b/src/anthropic/types/beta/beta_cache_control_ephemeral_param.py index 221a9f76c..cbddfaee2 100644 --- a/src/anthropic/types/beta/beta_cache_control_ephemeral_param.py +++ b/src/anthropic/types/beta/beta_cache_control_ephemeral_param.py @@ -18,5 +18,7 @@ class BetaCacheControlEphemeralParam(TypedDict, total=False): - `5m`: 5 minutes - `1h`: 1 hour - Defaults to `5m`. + Defaults to `5m`. See + [prompt caching pricing](https://docs.claude.com/en/docs/build-with-claude/prompt-caching) + for details. """ diff --git a/src/anthropic/types/beta/message_create_params.py b/src/anthropic/types/beta/message_create_params.py index aacd786b5..6a3ab448c 100644 --- a/src/anthropic/types/beta/message_create_params.py +++ b/src/anthropic/types/beta/message_create_params.py @@ -363,15 +363,16 @@ class MessageCreateParamsBase(TypedDict, total=False): Recommended for advanced use cases only. """ - user_profile_id: Optional[str] + betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] + """Optional header to specify the beta version(s) you want to use.""" + + user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute this request to. - Use when acting on behalf of a party other than your organization. + Use when acting on behalf of a party other than your organization. Requires the + `user-profiles` beta header. """ - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - Container: TypeAlias = Union[BetaContainerParams, str] diff --git a/src/anthropic/types/beta/messages/batch_create_params.py b/src/anthropic/types/beta/messages/batch_create_params.py index 13eeeb812..8ff6e9bef 100644 --- a/src/anthropic/types/beta/messages/batch_create_params.py +++ b/src/anthropic/types/beta/messages/batch_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import List, Iterable from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo @@ -22,10 +22,13 @@ class BatchCreateParams(TypedDict, total=False): betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" - user_profile_id: Optional[str] - """The user profile ID to attribute this request to. + user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] + """The user profile ID to attribute the requests in this batch to. - Use when acting on behalf of a party other than your organization. + Use when acting on behalf of a party other than your organization. Requires the + `user-profiles` beta header. Applies to every request in the batch; an + individual request whose `user_profile_id` body field conflicts with this header + is errored. """ diff --git a/src/anthropic/types/cache_control_ephemeral_param.py b/src/anthropic/types/cache_control_ephemeral_param.py index 0bdbe03cf..d8297c720 100644 --- a/src/anthropic/types/cache_control_ephemeral_param.py +++ b/src/anthropic/types/cache_control_ephemeral_param.py @@ -18,5 +18,7 @@ class CacheControlEphemeralParam(TypedDict, total=False): - `5m`: 5 minutes - `1h`: 1 hour - Defaults to `5m`. + Defaults to `5m`. See + [prompt caching pricing](https://docs.claude.com/en/docs/build-with-claude/prompt-caching) + for details. """ diff --git a/src/anthropic/types/message_create_params.py b/src/anthropic/types/message_create_params.py index 47d6a747b..64c82d04e 100644 --- a/src/anthropic/types/message_create_params.py +++ b/src/anthropic/types/message_create_params.py @@ -3,9 +3,10 @@ from __future__ import annotations from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr +from .._utils import PropertyInfo from .model_param import ModelParam from .message_param import MessageParam from .metadata_param import MetadataParam @@ -298,6 +299,13 @@ class MessageCreateParamsBase(TypedDict, total=False): Recommended for advanced use cases only. """ + user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] + """The user profile ID to attribute this request to. + + Use when acting on behalf of a party other than your organization. Requires the + `user-profiles` beta header. + """ + Metadata: TypeAlias = MetadataParam """This is deprecated, `MetadataParam` should be used instead""" diff --git a/src/anthropic/types/messages/batch_create_params.py b/src/anthropic/types/messages/batch_create_params.py index e60c74f13..e4cc87887 100644 --- a/src/anthropic/types/messages/batch_create_params.py +++ b/src/anthropic/types/messages/batch_create_params.py @@ -3,8 +3,9 @@ from __future__ import annotations from typing import Iterable -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict +from ..._utils import PropertyInfo from ..message_create_params import MessageCreateParamsNonStreaming __all__ = ["BatchCreateParams", "Request"] @@ -17,6 +18,15 @@ class BatchCreateParams(TypedDict, total=False): Each is an individual request to create a Message. """ + user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] + """The user profile ID to attribute the requests in this batch to. + + Use when acting on behalf of a party other than your organization. Requires the + `user-profiles` beta header. Applies to every request in the batch; an + individual request whose `user_profile_id` body field conflicts with this header + is errored. + """ + class Request(TypedDict, total=False): custom_id: Required[str] diff --git a/tests/api_resources/beta/messages/test_batches.py b/tests/api_resources/beta/messages/test_batches.py index da8aaa272..cfe674d88 100644 --- a/tests/api_resources/beta/messages/test_batches.py +++ b/tests/api_resources/beta/messages/test_batches.py @@ -211,11 +211,11 @@ def test_method_create_with_all_params(self, client: Anthropic) -> None: ], "top_k": 5, "top_p": 0.7, - "user_profile_id": "user_profile_id", }, } ], betas=["string"], + user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @@ -701,11 +701,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) ], "top_k": 5, "top_p": 0.7, - "user_profile_id": "user_profile_id", }, } ], betas=["string"], + user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) diff --git a/tests/api_resources/beta/test_messages.py b/tests/api_resources/beta/test_messages.py index 85ff8c589..36a6cbc5d 100644 --- a/tests/api_resources/beta/test_messages.py +++ b/tests/api_resources/beta/test_messages.py @@ -192,8 +192,8 @@ def test_method_create_with_all_params_overload_1(self, client: Anthropic) -> No ], top_k=5, top_p=0.7, - user_profile_id="user_profile_id", betas=["message-batches-2024-09-24"], + user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessage, message, path=["response"]) @@ -407,8 +407,8 @@ def test_method_create_with_all_params_overload_2(self, client: Anthropic) -> No ], top_k=5, top_p=0.7, - user_profile_id="user_profile_id", betas=["message-batches-2024-09-24"], + user_profile_id="anthropic-user-profile-id", ) message_stream.response.close() @@ -810,8 +810,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ], top_k=5, top_p=0.7, - user_profile_id="user_profile_id", betas=["message-batches-2024-09-24"], + user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessage, message, path=["response"]) @@ -1025,8 +1025,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn ], top_k=5, top_p=0.7, - user_profile_id="user_profile_id", betas=["message-batches-2024-09-24"], + user_profile_id="anthropic-user-profile-id", ) await message_stream.response.aclose() diff --git a/tests/api_resources/messages/test_batches.py b/tests/api_resources/messages/test_batches.py index 59e5f1d5a..ada941588 100644 --- a/tests/api_resources/messages/test_batches.py +++ b/tests/api_resources/messages/test_batches.py @@ -44,6 +44,100 @@ def test_method_create(self, client: Anthropic) -> None: ) assert_matches_type(MessageBatch, batch, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: Anthropic) -> None: + batch = client.messages.batches.create( + requests=[ + { + "custom_id": "my-custom-id-1", + "params": { + "max_tokens": 1024, + "messages": [ + { + "content": "Hello, world", + "role": "user", + } + ], + "model": "claude-opus-4-6", + "cache_control": { + "type": "ephemeral", + "ttl": "5m", + }, + "container": "container", + "inference_geo": "inference_geo", + "metadata": {"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, + "output_config": { + "effort": "low", + "format": { + "schema": {"foo": "bar"}, + "type": "json_schema", + }, + }, + "service_tier": "auto", + "stop_sequences": ["string"], + "stream": False, + "system": [ + { + "text": "Today's date is 2024-06-01.", + "type": "text", + "cache_control": { + "type": "ephemeral", + "ttl": "5m", + }, + "citations": [ + { + "cited_text": "cited_text", + "document_index": 0, + "document_title": "x", + "end_char_index": 0, + "start_char_index": 0, + "type": "char_location", + } + ], + } + ], + "temperature": 1, + "thinking": { + "type": "adaptive", + "display": "summarized", + }, + "tool_choice": { + "type": "auto", + "disable_parallel_tool_use": True, + }, + "tools": [ + { + "input_schema": { + "type": "object", + "properties": { + "location": "bar", + "unit": "bar", + }, + "required": ["location"], + }, + "name": "name", + "allowed_callers": ["direct"], + "cache_control": { + "type": "ephemeral", + "ttl": "5m", + }, + "defer_loading": True, + "description": "Get the current weather in a given location", + "eager_input_streaming": True, + "input_examples": [{"foo": "bar"}], + "strict": True, + "type": "custom", + } + ], + "top_k": 5, + "top_p": 0.7, + }, + } + ], + user_profile_id="anthropic-user-profile-id", + ) + assert_matches_type(MessageBatch, batch, path=["response"]) + @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.messages.batches.with_raw_response.create( @@ -301,6 +395,100 @@ async def test_method_create(self, async_client: AsyncAnthropic) -> None: ) assert_matches_type(MessageBatch, batch, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: + batch = await async_client.messages.batches.create( + requests=[ + { + "custom_id": "my-custom-id-1", + "params": { + "max_tokens": 1024, + "messages": [ + { + "content": "Hello, world", + "role": "user", + } + ], + "model": "claude-opus-4-6", + "cache_control": { + "type": "ephemeral", + "ttl": "5m", + }, + "container": "container", + "inference_geo": "inference_geo", + "metadata": {"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, + "output_config": { + "effort": "low", + "format": { + "schema": {"foo": "bar"}, + "type": "json_schema", + }, + }, + "service_tier": "auto", + "stop_sequences": ["string"], + "stream": False, + "system": [ + { + "text": "Today's date is 2024-06-01.", + "type": "text", + "cache_control": { + "type": "ephemeral", + "ttl": "5m", + }, + "citations": [ + { + "cited_text": "cited_text", + "document_index": 0, + "document_title": "x", + "end_char_index": 0, + "start_char_index": 0, + "type": "char_location", + } + ], + } + ], + "temperature": 1, + "thinking": { + "type": "adaptive", + "display": "summarized", + }, + "tool_choice": { + "type": "auto", + "disable_parallel_tool_use": True, + }, + "tools": [ + { + "input_schema": { + "type": "object", + "properties": { + "location": "bar", + "unit": "bar", + }, + "required": ["location"], + }, + "name": "name", + "allowed_callers": ["direct"], + "cache_control": { + "type": "ephemeral", + "ttl": "5m", + }, + "defer_loading": True, + "description": "Get the current weather in a given location", + "eager_input_streaming": True, + "input_examples": [{"foo": "bar"}], + "strict": True, + "type": "custom", + } + ], + "top_k": 5, + "top_p": 0.7, + }, + } + ], + user_profile_id="anthropic-user-profile-id", + ) + assert_matches_type(MessageBatch, batch, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.batches.with_raw_response.create( diff --git a/tests/api_resources/test_messages.py b/tests/api_resources/test_messages.py index 63963ed67..baabd9854 100644 --- a/tests/api_resources/test_messages.py +++ b/tests/api_resources/test_messages.py @@ -118,6 +118,7 @@ def test_method_create_with_all_params_overload_1(self, client: Anthropic) -> No ], top_k=5, top_p=0.7, + user_profile_id="anthropic-user-profile-id", ) assert_matches_type(Message, message, path=["response"]) @@ -257,6 +258,7 @@ def test_method_create_with_all_params_overload_2(self, client: Anthropic) -> No ], top_k=5, top_p=0.7, + user_profile_id="anthropic-user-profile-id", ) message_stream.response.close() @@ -537,6 +539,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ], top_k=5, top_p=0.7, + user_profile_id="anthropic-user-profile-id", ) assert_matches_type(Message, message, path=["response"]) @@ -676,6 +679,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn ], top_k=5, top_p=0.7, + user_profile_id="anthropic-user-profile-id", ) await message_stream.response.aclose() diff --git a/tests/lib/tools/memory_tools/test_filesystem.py b/tests/lib/tools/memory_tools/test_filesystem.py index 9a9398f40..0ab3c8fa9 100644 --- a/tests/lib/tools/memory_tools/test_filesystem.py +++ b/tests/lib/tools/memory_tools/test_filesystem.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import stat import tempfile from typing import Iterator from pathlib import Path @@ -51,6 +52,27 @@ def get_directory_snapshot(base_path: str) -> dict[str, str]: class TestBetaLocalFilesystemMemoryTool: + def test_mkdir_parents_not_world_writable_under_permissive_umask(self) -> None: + if os.name != "posix": + pytest.skip("POSIX mode bits only") + # Permissive umask: newly created dirs would default to 0o777 unless an + # explicit mode is enforced on every component of the tree. + old_umask = os.umask(0) + try: + with tempfile.TemporaryDirectory() as tmp: + # base_path's parent ("nested") does NOT exist yet, so the tool must + # create it as an intermediate parent. The bug: pathlib applies the + # 0o700 mode only to the leaf, leaving these intermediate parents at + # the umask default (0o777 here) — world-writable. + base_path = Path(tmp) / "nested" / "memory" + tool = BetaLocalFilesystemMemoryTool(base_path=str(base_path)) + for directory in (base_path.parent, base_path, tool.memory_root): + mode = stat.S_IMODE(directory.stat().st_mode) + assert mode == 0o700, f"{directory} has mode {oct(mode)}, expected 0o700" + assert not (mode & 0o077), f"{directory} is group/other-accessible: {oct(mode)}" + finally: + os.umask(old_umask) + def test_create(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: result = sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( @@ -518,6 +540,23 @@ def test_symlink_validation_reject_parent_directory_that_is_symlink_pointing_out class TestBetaAsyncLocalFilesystemMemoryTool: + async def test_mkdir_parents_not_world_writable_under_permissive_umask(self) -> None: + if os.name != "posix": + pytest.skip("POSIX mode bits only") + old_umask = os.umask(0) + try: + with tempfile.TemporaryDirectory() as tmp: + base_path = Path(tmp) / "nested" / "memory" + tool = BetaAsyncLocalFilesystemMemoryTool(base_path=str(base_path)) + await tool._ensure_memory_root() # triggers lazy directory creation + memory_root = Path(str(tool.memory_root)) + for directory in (base_path.parent, base_path, memory_root): + mode = stat.S_IMODE(directory.stat().st_mode) + assert mode == 0o700, f"{directory} has mode {oct(mode)}, expected 0o700" + assert not (mode & 0o077), f"{directory} is group/other-accessible: {oct(mode)}" + finally: + os.umask(old_umask) + async def test_create(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: result = await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(