Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: 0.31.0 #573

Merged
merged 11 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}

USER vscode

RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash
RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash
ENV PATH=/home/vscode/.rye/shims:$PATH

RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: 0.24.0
RYE_VERSION: '0.35.0'
RYE_INSTALL_OPTION: '--yes'

- name: Install dependencies
Expand All @@ -42,7 +42,7 @@ jobs:
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: 0.24.0
RYE_VERSION: '0.35.0'
RYE_INSTALL_OPTION: '--yes'

- name: Bootstrap
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/create-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: 0.24.0
RYE_INSTALL_OPTION: "--yes"
RYE_VERSION: '0.35.0'
RYE_INSTALL_OPTION: '--yes'

- name: Publish to PyPI
if: ${{ steps.release.outputs.releases_created }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ jobs:
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
RYE_VERSION: 0.24.0
RYE_INSTALL_OPTION: "--yes"
RYE_VERSION: '0.35.0'
RYE_INSTALL_OPTION: '--yes'

- name: Publish to PyPI
run: |
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.30.1"
".": "0.31.0"
}
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Changelog

## 0.31.0 (2024-07-10)

Full Changelog: [v0.30.1...v0.31.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.30.1...v0.31.0)

### Features

* **client:** make request-id header more accessible ([#581](https://github.com/anthropics/anthropic-sdk-python/issues/581)) ([130d470](https://github.com/anthropics/anthropic-sdk-python/commit/130d470fc624a25defb9d8e787462b77bdc0aad5))
* **vertex:** add copy and with_options ([#578](https://github.com/anthropics/anthropic-sdk-python/issues/578)) ([fcd425f](https://github.com/anthropics/anthropic-sdk-python/commit/fcd425f724fee45195118aa218bd5c51fb9abed0))


### Bug Fixes

* **client:** always respect content-type multipart/form-data if provided ([#574](https://github.com/anthropics/anthropic-sdk-python/issues/574)) ([6051763](https://github.com/anthropics/anthropic-sdk-python/commit/6051763d886aa7107389d8b8aeacf74d296eed3d))
* **streaming/messages:** more robust event type construction ([#576](https://github.com/anthropics/anthropic-sdk-python/issues/576)) ([98e2075](https://github.com/anthropics/anthropic-sdk-python/commit/98e2075869d816cd85af1a0588bd27719eff02a4))
* **types:** allow arbitrary types in image block param ([#582](https://github.com/anthropics/anthropic-sdk-python/issues/582)) ([ebd6590](https://github.com/anthropics/anthropic-sdk-python/commit/ebd659014b63b51fa2f67fe88ef3fc9922be830d))
* Updated doc typo ([17be06b](https://github.com/anthropics/anthropic-sdk-python/commit/17be06bf3e39eff9de588d99cd59fa509c5ee6a6))
* **vertex:** avoid credentials refresh on every request ([#575](https://github.com/anthropics/anthropic-sdk-python/issues/575)) ([37bd433](https://github.com/anthropics/anthropic-sdk-python/commit/37bd4337828f3efa14b194fa3025638229129416))


### Chores

* **ci:** update rye to v0.35.0 ([#577](https://github.com/anthropics/anthropic-sdk-python/issues/577)) ([e271d69](https://github.com/anthropics/anthropic-sdk-python/commit/e271d694babfb4bcb506064aa353ee29a8394c1d))
* **internal:** add helper method for constructing `BaseModel`s ([#572](https://github.com/anthropics/anthropic-sdk-python/issues/572)) ([8e626ac](https://github.com/anthropics/anthropic-sdk-python/commit/8e626ac7c88bab413bc1e2d83b7556aa4a44fb63))
* **internal:** fix formatting ([a912917](https://github.com/anthropics/anthropic-sdk-python/commit/a912917686d6e4a46d192abf002ac69357b1d955))
* **internal:** minor request options handling changes ([#580](https://github.com/anthropics/anthropic-sdk-python/issues/580)) ([d1dcf42](https://github.com/anthropics/anthropic-sdk-python/commit/d1dcf427ea78f57dd267d891c276b03d4010de78))

## 0.30.1 (2024-07-01)

Full Changelog: [v0.30.0...v0.30.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.30.0...v0.30.1)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "anthropic"
version = "0.30.1"
version = "0.31.0"
description = "The official Python library for the anthropic API"
dynamic = ["readme"]
license = "MIT"
Expand Down
1 change: 1 addition & 0 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# features: []
# all-features: true
# with-sources: false
# generate-hashes: false

-e file:.
annotated-types==0.6.0
Expand Down
1 change: 1 addition & 0 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# features: []
# all-features: true
# with-sources: false
# generate-hashes: false

-e file:.
annotated-types==0.6.0
Expand Down
43 changes: 35 additions & 8 deletions src/anthropic/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
HttpxSendArgs,
AsyncTransport,
RequestOptions,
HttpxRequestFiles,
ModelBuilderProtocol,
)
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
Expand Down Expand Up @@ -460,6 +461,7 @@ def _build_request(
headers = self._build_headers(options)
params = _merge_mappings(self.default_query, options.params)
content_type = headers.get("Content-Type")
files = options.files

# If the given Content-Type header is multipart/form-data then it
# has to be removed so that httpx can generate the header with
Expand All @@ -473,14 +475,23 @@ def _build_request(
headers.pop("Content-Type")

# As we are now sending multipart/form-data instead of application/json
# we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding
# we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding
if json_data:
if not is_dict(json_data):
raise TypeError(
f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead."
)
kwargs["data"] = self._serialize_multipartform(json_data)

# httpx determines whether or not to send a "multipart/form-data"
# request based on the truthiness of the "files" argument.
# This gets around that issue by generating a dict value that
# evaluates to true.
#
# https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186
if not files:
files = cast(HttpxRequestFiles, ForceMultipartDict())

# TODO: report this error to httpx
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
headers=headers,
Expand All @@ -493,7 +504,7 @@ def _build_request(
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
json=json_data,
files=options.files,
files=files,
**kwargs,
)

Expand Down Expand Up @@ -945,6 +956,11 @@ def _request(
stream: bool,
stream_cls: type[_StreamT] | None,
) -> ResponseT | _StreamT:
# create a copy of the options we were given so that if the
# options are mutated later & we then retry, the retries are
# given the original options
input_options = model_copy(options)

cast_to = self._maybe_override_cast_to(cast_to, options)
self._prepare_options(options)

Expand All @@ -969,7 +985,7 @@ def _request(

if retries > 0:
return self._retry_request(
options,
input_options,
cast_to,
retries,
stream=stream,
Expand All @@ -984,7 +1000,7 @@ def _request(

if retries > 0:
return self._retry_request(
options,
input_options,
cast_to,
retries,
stream=stream,
Expand All @@ -1003,6 +1019,7 @@ def _request(
response.reason_phrase,
response.headers,
)
log.debug("request_id: %s", response.headers.get("request-id"))

try:
response.raise_for_status()
Expand All @@ -1012,7 +1029,7 @@ def _request(
if retries > 0 and self._should_retry(err.response):
err.response.close()
return self._retry_request(
options,
input_options,
cast_to,
retries,
err.response.headers,
Expand Down Expand Up @@ -1521,6 +1538,11 @@ async def _request(
# execute it earlier while we are in an async context
self._platform = await asyncify(get_platform)()

# create a copy of the options we were given so that if the
# options are mutated later & we then retry, the retries are
# given the original options
input_options = model_copy(options)

cast_to = self._maybe_override_cast_to(cast_to, options)
await self._prepare_options(options)

Expand All @@ -1543,7 +1565,7 @@ async def _request(

if retries > 0:
return await self._retry_request(
options,
input_options,
cast_to,
retries,
stream=stream,
Expand All @@ -1558,7 +1580,7 @@ async def _request(

if retries > 0:
return await self._retry_request(
options,
input_options,
cast_to,
retries,
stream=stream,
Expand All @@ -1581,7 +1603,7 @@ async def _request(
if retries > 0 and self._should_retry(err.response):
await err.response.aclose()
return await self._retry_request(
options,
input_options,
cast_to,
retries,
err.response.headers,
Expand Down Expand Up @@ -1890,6 +1912,11 @@ def make_request_options(
return options


class ForceMultipartDict(Dict[str, None]):
def __bool__(self) -> bool:
return True


class OtherPlatform:
def __init__(self, name: str) -> None:
self.name = name
Expand Down
2 changes: 2 additions & 0 deletions src/anthropic/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,13 @@ class APIStatusError(APIError):

response: httpx.Response
status_code: int
request_id: str | None

def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None:
super().__init__(message, response.request, body=body)
self.response = response
self.status_code = response.status_code
self.request_id = response.headers.get("request-id")


class APIConnectionError(APIError):
Expand Down
4 changes: 4 additions & 0 deletions src/anthropic/_legacy_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ def __init__(
self._options = options
self.http_response = raw

@property
def request_id(self) -> str | None:
return self.http_response.headers.get("request-id") # type: ignore[no-any-return]

@overload
def parse(self, *, to: type[_T]) -> _T:
...
Expand Down
35 changes: 35 additions & 0 deletions src/anthropic/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ClassVar,
Protocol,
Required,
ParamSpec,
TypedDict,
TypeGuard,
final,
Expand Down Expand Up @@ -67,6 +68,9 @@
__all__ = ["BaseModel", "GenericModel"]

_T = TypeVar("_T")
_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel")

P = ParamSpec("P")


@runtime_checkable
Expand Down Expand Up @@ -379,6 +383,29 @@ def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericMo
return issubclass(origin, BaseModel) or issubclass(origin, GenericModel)


def build(
base_model_cls: Callable[P, _BaseModelT],
*args: P.args,
**kwargs: P.kwargs,
) -> _BaseModelT:
"""Construct a BaseModel class without validation.

This is useful for cases where you need to instantiate a `BaseModel`
from an API response as this provides type-safe params which isn't supported
by helpers like `construct_type()`.

```py
build(MyModel, my_field_a="foo", my_field_b=123)
```
"""
if args:
raise TypeError(
"Received positional arguments which are not supported; Keyword arguments must be used instead",
)

return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs))


def construct_type(*, value: object, type_: object) -> object:
"""Loose coercion to the expected type with construction of nested values.

Expand Down Expand Up @@ -616,6 +643,14 @@ def validate_type(*, type_: type[_T], value: object) -> _T:
return cast(_T, _validate_non_model_type(type_=type_, value=value))


def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None:
"""Add a pydantic config for the given type.

Note: this is a no-op on Pydantic v1.
"""
setattr(typ, "__pydantic_config__", config) # noqa: B010


# our use of subclasssing here causes weirdness for type checkers,
# so we just pretend that we don't subclass
if TYPE_CHECKING:
Expand Down
8 changes: 8 additions & 0 deletions src/anthropic/_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:


class APIResponse(BaseAPIResponse[R]):
@property
def request_id(self) -> str | None:
return self.http_response.headers.get("request-id") # type: ignore[no-any-return]

@overload
def parse(self, *, to: type[_T]) -> _T:
...
Expand Down Expand Up @@ -362,6 +366,10 @@ def iter_lines(self) -> Iterator[str]:


class AsyncAPIResponse(BaseAPIResponse[R]):
@property
def request_id(self) -> str | None:
return self.http_response.headers.get("request-id") # type: ignore[no-any-return]

@overload
async def parse(self, *, to: type[_T]) -> _T:
...
Expand Down
2 changes: 1 addition & 1 deletion src/anthropic/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "anthropic"
__version__ = "0.30.1" # x-release-please-version
__version__ = "0.31.0" # x-release-please-version
Loading
Loading