anthropic 0.72.1__py3-none-any.whl → 0.74.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. anthropic/__init__.py +3 -0
  2. anthropic/_client.py +8 -0
  3. anthropic/_compat.py +6 -0
  4. anthropic/_models.py +13 -1
  5. anthropic/_utils/_transform.py +1 -1
  6. anthropic/_version.py +1 -1
  7. anthropic/lib/_files.py +2 -2
  8. anthropic/lib/_parse/_response.py +44 -0
  9. anthropic/lib/_parse/_transform.py +167 -0
  10. anthropic/lib/foundry.md +127 -0
  11. anthropic/lib/foundry.py +443 -0
  12. anthropic/lib/streaming/__init__.py +14 -4
  13. anthropic/lib/streaming/_beta_messages.py +82 -43
  14. anthropic/lib/streaming/_beta_types.py +21 -13
  15. anthropic/lib/tools/_beta_runner.py +102 -101
  16. anthropic/resources/beta/messages/batches.py +12 -12
  17. anthropic/resources/beta/messages/messages.py +365 -29
  18. anthropic/resources/messages/batches.py +12 -12
  19. anthropic/resources/messages/messages.py +14 -8
  20. anthropic/types/beta/__init__.py +1 -0
  21. anthropic/types/beta/beta_code_execution_tool_20250522_param.py +2 -0
  22. anthropic/types/beta/beta_code_execution_tool_20250825_param.py +2 -0
  23. anthropic/types/beta/beta_json_output_format_param.py +15 -0
  24. anthropic/types/beta/beta_memory_tool_20250818_param.py +2 -0
  25. anthropic/types/beta/beta_tool_bash_20241022_param.py +2 -0
  26. anthropic/types/beta/beta_tool_bash_20250124_param.py +2 -0
  27. anthropic/types/beta/beta_tool_computer_use_20241022_param.py +2 -0
  28. anthropic/types/beta/beta_tool_computer_use_20250124_param.py +2 -0
  29. anthropic/types/beta/beta_tool_param.py +2 -0
  30. anthropic/types/beta/beta_tool_text_editor_20241022_param.py +2 -0
  31. anthropic/types/beta/beta_tool_text_editor_20250124_param.py +2 -0
  32. anthropic/types/beta/beta_tool_text_editor_20250429_param.py +2 -0
  33. anthropic/types/beta/beta_tool_text_editor_20250728_param.py +2 -0
  34. anthropic/types/beta/beta_web_fetch_tool_20250910_param.py +2 -0
  35. anthropic/types/beta/beta_web_search_tool_20250305_param.py +2 -0
  36. anthropic/types/beta/message_count_tokens_params.py +4 -0
  37. anthropic/types/beta/message_create_params.py +24 -2
  38. anthropic/types/beta/messages/batch_create_params.py +8 -2
  39. anthropic/types/beta/parsed_beta_message.py +68 -0
  40. anthropic/types/messages/batch_create_params.py +0 -1
  41. {anthropic-0.72.1.dist-info → anthropic-0.74.0.dist-info}/METADATA +1 -1
  42. {anthropic-0.72.1.dist-info → anthropic-0.74.0.dist-info}/RECORD +44 -38
  43. {anthropic-0.72.1.dist-info → anthropic-0.74.0.dist-info}/WHEEL +0 -0
  44. {anthropic-0.72.1.dist-info → anthropic-0.74.0.dist-info}/licenses/LICENSE +0 -0
anthropic/__init__.py CHANGED
@@ -44,6 +44,7 @@ from ._exceptions import (
44
44
  )
45
45
  from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
46
46
  from ._utils._logs import setup_logging as _setup_logging
47
+ from .lib._parse._transform import transform_schema
47
48
 
48
49
  __all__ = [
49
50
  "types",
@@ -91,6 +92,7 @@ __all__ = [
91
92
  "AI_PROMPT",
92
93
  "beta_tool",
93
94
  "beta_async_tool",
95
+ "transform_schema",
94
96
  ]
95
97
 
96
98
  if not _t.TYPE_CHECKING:
@@ -99,6 +101,7 @@ if not _t.TYPE_CHECKING:
99
101
  from .lib.tools import beta_tool, beta_async_tool
100
102
  from .lib.vertex import *
101
103
  from .lib.bedrock import *
104
+ from .lib.foundry import AnthropicFoundry as AnthropicFoundry, AsyncAnthropicFoundry as AsyncAnthropicFoundry
102
105
  from .lib.streaming import *
103
106
 
104
107
  _setup_logging()
anthropic/_client.py CHANGED
@@ -183,6 +183,10 @@ class Anthropic(SyncAPIClient):
183
183
 
184
184
  @override
185
185
  def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
186
+ if headers.get("Authorization") or headers.get("X-Api-Key"):
187
+ # valid
188
+ return
189
+
186
190
  if self.api_key and headers.get("X-Api-Key"):
187
191
  return
188
192
  if isinstance(custom_headers.get("X-Api-Key"), Omit):
@@ -423,6 +427,10 @@ class AsyncAnthropic(AsyncAPIClient):
423
427
 
424
428
  @override
425
429
  def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
430
+ if headers.get("Authorization") or headers.get("X-Api-Key"):
431
+ # valid
432
+ return
433
+
426
434
  if self.api_key and headers.get("X-Api-Key"):
427
435
  return
428
436
  if isinstance(custom_headers.get("X-Api-Key"), Omit):
anthropic/_compat.py CHANGED
@@ -131,6 +131,12 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
131
131
  return model.model_dump_json(indent=indent)
132
132
 
133
133
 
134
+ def model_parse_json(model: type[_ModelT], data: str | bytes) -> _ModelT:
135
+ if PYDANTIC_V1:
136
+ return model.parse_raw(data) # pyright: ignore[reportDeprecated]
137
+ return model.model_validate_json(data)
138
+
139
+
134
140
  def model_dump(
135
141
  model: pydantic.BaseModel,
136
142
  *,
anthropic/_models.py CHANGED
@@ -774,7 +774,7 @@ else:
774
774
 
775
775
 
776
776
  if not PYDANTIC_V1:
777
- from pydantic import TypeAdapter as _TypeAdapter
777
+ from pydantic import TypeAdapter as _TypeAdapter, computed_field as computed_field
778
778
 
779
779
  _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter))
780
780
 
@@ -811,6 +811,18 @@ elif not TYPE_CHECKING: # TODO: condition is weird
811
811
  def TypeAdapter(*_args: Any, **_kwargs: Any) -> Any:
812
812
  raise RuntimeError("attempted to use TypeAdapter in pydantic v1")
813
813
 
814
+ def computed_field(func: Any | None = None, /, **__: Any) -> Any:
815
+ def _exc_func(*_: Any, **__: Any) -> Any:
816
+ raise RuntimeError("attempted to use computed_field in pydantic v1")
817
+
818
+ def _dec(*_: Any, **__: Any) -> Any:
819
+ return _exc_func
820
+
821
+ if func is not None:
822
+ return _dec(func)
823
+ else:
824
+ return _dec
825
+
814
826
 
815
827
  class FinalRequestOptionsInput(TypedDict, total=False):
816
828
  method: Required[str]
@@ -218,7 +218,7 @@ def _transform_recursive(
218
218
  return data
219
219
 
220
220
  if isinstance(data, pydantic.BaseModel):
221
- return model_dump(data, exclude_unset=True, mode="json")
221
+ return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))
222
222
 
223
223
  annotated_type = _get_annotated_type(annotation)
224
224
  if annotated_type is None:
anthropic/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "anthropic"
4
- __version__ = "0.72.1" # x-release-please-version
4
+ __version__ = "0.74.0" # x-release-please-version
anthropic/lib/_files.py CHANGED
@@ -22,7 +22,7 @@ def _collect_files(directory: Path, relative_to: Path, files: list[FileTypes]) -
22
22
  _collect_files(path, relative_to, files)
23
23
  continue
24
24
 
25
- files.append((str(path.relative_to(relative_to)), path.read_bytes()))
25
+ files.append((path.relative_to(relative_to).as_posix(), path.read_bytes()))
26
26
 
27
27
 
28
28
  async def async_files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]:
@@ -39,4 +39,4 @@ async def _async_collect_files(directory: anyio.Path, relative_to: anyio.Path, f
39
39
  await _async_collect_files(path, relative_to, files)
40
40
  continue
41
41
 
42
- files.append((str(path.relative_to(relative_to)), await path.read_bytes()))
42
+ files.append((path.relative_to(relative_to).as_posix(), await path.read_bytes()))
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from typing_extensions import TypeVar
4
+
5
+ from ..._types import NotGiven
6
+ from ..._models import TypeAdapter, construct_type_unchecked
7
+ from ..._utils._utils import is_given
8
+ from ...types.beta.beta_message import BetaMessage
9
+ from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaTextBlock, ParsedBetaContentBlock
10
+
11
+ ResponseFormatT = TypeVar("ResponseFormatT", default=None)
12
+
13
+
14
+ def parse_text(text: str, output_format: ResponseFormatT | NotGiven) -> ResponseFormatT | None:
15
+ if is_given(output_format):
16
+ adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format)
17
+ return adapted_type.validate_json(text)
18
+ return None
19
+
20
+
21
+ def parse_response(
22
+ *,
23
+ output_format: ResponseFormatT | NotGiven,
24
+ response: BetaMessage,
25
+ ) -> ParsedBetaMessage[ResponseFormatT]:
26
+ content_list: list[ParsedBetaContentBlock[ResponseFormatT]] = []
27
+ for content in response.content:
28
+ if content.type == "text":
29
+ content_list.append(
30
+ construct_type_unchecked(
31
+ type_=ParsedBetaTextBlock[ResponseFormatT],
32
+ value={**content.to_dict(), "parsed_output": parse_text(content.text, output_format)},
33
+ )
34
+ )
35
+ else:
36
+ content_list.append(content) # type: ignore
37
+
38
+ return construct_type_unchecked(
39
+ type_=ParsedBetaMessage[ResponseFormatT],
40
+ value={
41
+ **response.to_dict(),
42
+ "content": content_list,
43
+ },
44
+ )
@@ -0,0 +1,167 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import Any, Literal, Optional, cast
5
+ from typing_extensions import assert_never
6
+
7
+ import pydantic
8
+
9
+ from ..._utils import is_list
10
+
11
+ SupportedTypes = Literal[
12
+ "object",
13
+ "array",
14
+ "string",
15
+ "integer",
16
+ "number",
17
+ "boolean",
18
+ "null",
19
+ ]
20
+
21
+ SupportedStringFormats = {
22
+ "date-time",
23
+ "time",
24
+ "date",
25
+ "duration",
26
+ "email",
27
+ "hostname",
28
+ "uri",
29
+ "ipv4",
30
+ "ipv6",
31
+ "uuid",
32
+ }
33
+
34
+
35
+ def get_transformed_string(
36
+ schema: dict[str, Any],
37
+ ) -> dict[str, Any]:
38
+ """Transforms a JSON schema of type string to ensure it conforms to the API's expectations.
39
+
40
+ Specifically, it ensures that if the schema is of type "string" and does not already
41
+ specify a "format", it sets the format to "text".
42
+
43
+ Args:
44
+ schema: The original JSON schema.
45
+
46
+ Returns:
47
+ The transformed JSON schema.
48
+ """
49
+ if schema.get("type") == "string" and "format" not in schema:
50
+ schema["format"] = "text"
51
+ return schema
52
+
53
+
54
+ def transform_schema(
55
+ json_schema: type[pydantic.BaseModel] | dict[str, Any],
56
+ ) -> dict[str, Any]:
57
+ """
58
+ Transforms a JSON schema to ensure it conforms to the API's expectations.
59
+
60
+ Args:
61
+ json_schema (Dict[str, Any]): The original JSON schema.
62
+
63
+ Returns:
64
+ The transformed JSON schema.
65
+
66
+ Examples:
67
+ >>> transform_schema(
68
+ ... {
69
+ ... "type": "integer",
70
+ ... "minimum": 1,
71
+ ... "maximum": 10,
72
+ ... "description": "A number",
73
+ ... }
74
+ ... )
75
+ {'type': 'integer', 'description': 'A number\n\n{minimum: 1, maximum: 10}'}
76
+ """
77
+ if inspect.isclass(json_schema) and issubclass(json_schema, pydantic.BaseModel): # pyright: ignore[reportUnnecessaryIsInstance]
78
+ json_schema = json_schema.model_json_schema()
79
+
80
+ strict_schema: dict[str, Any] = {}
81
+ json_schema = {**json_schema}
82
+
83
+ ref = json_schema.pop("$ref", None)
84
+ if ref is not None:
85
+ strict_schema["$ref"] = ref
86
+ return strict_schema
87
+
88
+ defs = json_schema.pop("$defs", None)
89
+ if defs is not None:
90
+ strict_defs: dict[str, Any] = {}
91
+ strict_schema["$defs"] = strict_defs
92
+
93
+ for name, schema in defs.items():
94
+ strict_defs[name] = transform_schema(schema)
95
+
96
+ type_: Optional[SupportedTypes] = json_schema.pop("type", None)
97
+ any_of = json_schema.pop("anyOf", None)
98
+ one_of = json_schema.pop("oneOf", None)
99
+ all_of = json_schema.pop("allOf", None)
100
+
101
+ if is_list(any_of):
102
+ strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in any_of]
103
+ elif is_list(one_of):
104
+ strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in one_of]
105
+ elif is_list(all_of):
106
+ strict_schema["allOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in all_of]
107
+ else:
108
+ if type_ is None:
109
+ raise ValueError("Schema must have a 'type', 'anyOf', 'oneOf', or 'allOf' field.")
110
+
111
+ strict_schema["type"] = type_
112
+
113
+ description = json_schema.pop("description", None)
114
+ if description is not None:
115
+ strict_schema["description"] = description
116
+
117
+ title = json_schema.pop("title", None)
118
+ if title is not None:
119
+ strict_schema["title"] = title
120
+
121
+ if type_ == "object":
122
+ strict_schema["properties"] = {
123
+ key: transform_schema(prop_schema) for key, prop_schema in json_schema.pop("properties", {}).items()
124
+ }
125
+ json_schema.pop("additionalProperties", None)
126
+ strict_schema["additionalProperties"] = False
127
+
128
+ required = json_schema.pop("required", None)
129
+ if required is not None:
130
+ strict_schema["required"] = required
131
+
132
+ elif type_ == "string":
133
+ format = json_schema.pop("format", None)
134
+ if format and format in SupportedStringFormats:
135
+ strict_schema["format"] = format
136
+ elif format:
137
+ # add it back so its treated as an extra property and appended to the description
138
+ json_schema["format"] = format
139
+ elif type_ == "array":
140
+ items = json_schema.pop("items", None)
141
+ if items is not None:
142
+ strict_schema["items"] = transform_schema(items)
143
+
144
+ min_items = json_schema.pop("minItems", None)
145
+ if min_items is not None and min_items == 0 or min_items == 1:
146
+ strict_schema["minItems"] = min_items
147
+ elif min_items is not None:
148
+ # add it back so its treated as an extra property and appended to the description
149
+ json_schema["minItems"] = min_items
150
+
151
+ elif type_ == "boolean" or type_ == "integer" or type_ == "number" or type_ == "null" or type_ is None:
152
+ pass
153
+ else:
154
+ assert_never(type_)
155
+
156
+ # if there are any propes leftover then they aren't supported, so we add them to the description
157
+ # so that the model *might* follow them.
158
+ if json_schema:
159
+ description = strict_schema.get("description")
160
+ strict_schema["description"] = (
161
+ (description + "\n\n" if description is not None else "")
162
+ + "{"
163
+ + ", ".join(f"{key}: {value}" for key, value in json_schema.items())
164
+ + "}"
165
+ )
166
+
167
+ return strict_schema
@@ -0,0 +1,127 @@
1
+ # Anthropic Foundry
2
+
3
+ To use this library with Foundry, use the `AnthropicFoundry` class instead of the `Anthropic` class.
4
+
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pip install anthropic
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ### Basic Usage with API Key
15
+
16
+ ```python
17
+ from anthropic import AnthropicFoundry
18
+
19
+ client = AnthropicFoundry(
20
+ api_key="...", # defaults to ANTHROPIC_FOUNDRY_API_KEY environment variable
21
+ resource="my-resource", # your Foundry resource
22
+ )
23
+
24
+ message = client.messages.create(
25
+ model="claude-3-5-sonnet-20241022",
26
+ max_tokens=1024,
27
+ messages=[{"role": "user", "content": "Hello!"}],
28
+ )
29
+
30
+ print(message.content[0].text)
31
+ ```
32
+
33
+ ### Using Azure AD Token Provider
34
+
35
+ For enhanced security, you can use Azure AD (Microsoft Entra) authentication instead of an API key:
36
+
37
+ ```python
38
+ from anthropic import AnthropicFoundry
39
+ from azure.identity import DefaultAzureCredential
40
+ from azure.identity import get_bearer_token_provider
41
+
42
+ credential = DefaultAzureCredential()
43
+ token_provider = get_bearer_token_provider(
44
+ credential,
45
+ "https://ai.azure.com/.default"
46
+ )
47
+
48
+ client = AnthropicFoundry(
49
+ azure_ad_token_provider=token_provider,
50
+ resource="my-resource",
51
+ )
52
+
53
+ message = client.messages.create(
54
+ model="claude-3-5-sonnet-20241022",
55
+ max_tokens=1024,
56
+ messages=[{"role": "user", "content": "Hello!"}],
57
+ )
58
+
59
+ print(message.content[0].text)
60
+ ```
61
+
62
+ ## Examples
63
+
64
+ ### Streaming Messages
65
+
66
+ ```python
67
+ from anthropic import AnthropicFoundry
68
+
69
+ client = AnthropicFoundry(
70
+ api_key="...",
71
+ resource="my-resource",
72
+ )
73
+
74
+ with client.messages.stream(
75
+ model="claude-3-5-sonnet-20241022",
76
+ max_tokens=1024,
77
+ messages=[{"role": "user", "content": "Write a haiku about programming"}],
78
+ ) as stream:
79
+ for text in stream.text_stream:
80
+ print(text, end="", flush=True)
81
+ ```
82
+
83
+ ### Async Usage
84
+
85
+ ```python
86
+ from anthropic import AsyncAnthropicFoundry
87
+
88
+ async def main():
89
+ client = AsyncAnthropicFoundry(
90
+ api_key="...",
91
+ resource="my-resource",
92
+ )
93
+
94
+ message = await client.messages.create(
95
+ model="claude-3-5-sonnet-20241022",
96
+ max_tokens=1024,
97
+ messages=[{"role": "user", "content": "Hello!"}],
98
+ )
99
+
100
+ print(message.content[0].text)
101
+
102
+ import asyncio
103
+ asyncio.run(main())
104
+ ```
105
+
106
+ ### Async Streaming
107
+
108
+ ```python
109
+ from anthropic import AsyncAnthropicFoundry
110
+
111
+ async def main():
112
+ client = AsyncAnthropicFoundry(
113
+ api_key="...",
114
+ resource="my-resource",
115
+ )
116
+
117
+ async with client.messages.stream(
118
+ model="claude-3-5-sonnet-20241022",
119
+ max_tokens=1024,
120
+ messages=[{"role": "user", "content": "Write a haiku about programming"}],
121
+ ) as stream:
122
+ async for text in stream.text_stream:
123
+ print(text, end="", flush=True)
124
+
125
+ import asyncio
126
+ asyncio.run(main())
127
+ ```