diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 6eb00725..dd939620 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
USER vscode
-RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.15.2" RYE_INSTALL_OPTION="--yes" bash
+RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash
ENV PATH=/home/vscode/.rye/shims:$PATH
RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 18fcbaa3..a66523fa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,14 +14,14 @@ jobs:
if: github.repository == 'anthropics/anthropic-sdk-python'
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install Rye
run: |
curl -sSf https://rye-up.com/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
- RYE_VERSION: 0.15.2
+ RYE_VERSION: 0.24.0
RYE_INSTALL_OPTION: "--yes"
- name: Install dependencies
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
index 591166c2..093acc52 100644
--- a/.github/workflows/create-releases.yml
+++ b/.github/workflows/create-releases.yml
@@ -28,7 +28,7 @@ jobs:
curl -sSf https://rye-up.com/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
- RYE_VERSION: 0.15.2
+ RYE_VERSION: 0.24.0
RYE_INSTALL_OPTION: "--yes"
- name: Publish to PyPI
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index cfed9780..41d3e295 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -17,7 +17,7 @@ jobs:
curl -sSf https://rye-up.com/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
- RYE_VERSION: 0.15.2
+ RYE_VERSION: 0.24.0
RYE_INSTALL_OPTION: "--yes"
- name: Publish to PyPI
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index b4e9013b..6db19b95 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.16.0"
+ ".": "0.17.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98d39631..51d695eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,37 @@
# Changelog
+## 0.17.0 (2024-03-04)
+
+Full Changelog: [v0.16.0...v0.17.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.16.0...v0.17.0)
+
+### Features
+
+* **messages:** add support for image inputs ([#359](https://github.com/anthropics/anthropic-sdk-python/issues/359)) ([579f013](https://github.com/anthropics/anthropic-sdk-python/commit/579f013dd294f34b3c44e2b331a4aa25b6cdfd6a))
+
+
+### Chores
+
+* **client:** use anyio.sleep instead of asyncio.sleep ([#351](https://github.com/anthropics/anthropic-sdk-python/issues/351)) ([2778a22](https://github.com/anthropics/anthropic-sdk-python/commit/2778a2228e82704dde9176d970274e806422c02b))
+* **docs:** mention install from git repo ([#356](https://github.com/anthropics/anthropic-sdk-python/issues/356)) ([9d503ba](https://github.com/anthropics/anthropic-sdk-python/commit/9d503ba9cc462e33166594ca19f666819a3a5a87))
+* **docs:** remove references to old bedrock package ([#344](https://github.com/anthropics/anthropic-sdk-python/issues/344)) ([3323883](https://github.com/anthropics/anthropic-sdk-python/commit/3323883750b9d61fa084cadc99519b2f6cf8d39c))
+* **internal:** bump pyright ([#350](https://github.com/anthropics/anthropic-sdk-python/issues/350)) ([ee0161c](https://github.com/anthropics/anthropic-sdk-python/commit/ee0161c2d7d2fefd06ee5b1001131cd6d6d236d7))
+* **internal:** bump rye to v0.24.0 ([#348](https://github.com/anthropics/anthropic-sdk-python/issues/348)) ([be8597b](https://github.com/anthropics/anthropic-sdk-python/commit/be8597b33c2f2f6e8b9ae77738f4c898e48c8e91))
+* **internal:** improve bedrock streaming setup ([#354](https://github.com/anthropics/anthropic-sdk-python/issues/354)) ([2b55c68](https://github.com/anthropics/anthropic-sdk-python/commit/2b55c688514e4b13abce547362f0c0a3e7f0e97f))
+* **internal:** refactor release environment script ([#347](https://github.com/anthropics/anthropic-sdk-python/issues/347)) ([a87443a](https://github.com/anthropics/anthropic-sdk-python/commit/a87443a90374aedaac80451f61046c6f1aefeaa9))
+* **internal:** split up transforms into sync / async ([#357](https://github.com/anthropics/anthropic-sdk-python/issues/357)) ([f55ee71](https://github.com/anthropics/anthropic-sdk-python/commit/f55ee71b0b517f3e605bfd7a4aa948a9c2fc6552))
+* **internal:** support more input types ([#358](https://github.com/anthropics/anthropic-sdk-python/issues/358)) ([35b0347](https://github.com/anthropics/anthropic-sdk-python/commit/35b0347bfddecc94fc8ac09b42ff3d96f4523bf8))
+* **internal:** update deps ([#349](https://github.com/anthropics/anthropic-sdk-python/issues/349)) ([ab82b2d](https://github.com/anthropics/anthropic-sdk-python/commit/ab82b2d7ce16f3fed4b20e60f0c8e7981c22c191))
+
+
+### Documentation
+
+* **contributing:** improve wording ([#355](https://github.com/anthropics/anthropic-sdk-python/issues/355)) ([f9093a0](https://github.com/anthropics/anthropic-sdk-python/commit/f9093a0ee8d590185f572749d58280f7eda5ed8b))
+
+
+### Refactors
+
+* **api:** mark completions API as legacy ([#346](https://github.com/anthropics/anthropic-sdk-python/issues/346)) ([2bb25a1](https://github.com/anthropics/anthropic-sdk-python/commit/2bb25a12509b87557f3da2125ab955b60e32713f))
+
## 0.16.0 (2024-02-13)
Full Changelog: [v0.15.1...v0.16.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.15.1...v0.16.0)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4656e3e1..10854833 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -59,7 +59,7 @@ If you’d like to use the repository from source, you can either install from g
To install via git:
```bash
-pip install git+ssh://git@github.com:anthropics/anthropic-sdk-python.git
+pip install git+ssh://git@github.com/anthropics/anthropic-sdk-python.git
```
Alternatively, you can build from source and install the wheel file:
@@ -82,7 +82,7 @@ pip install ./path-to-wheel-file.whl
## Running tests
-Most tests will require you to [setup a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
+Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
```bash
# you will need npm installed
@@ -117,7 +117,7 @@ the changes aren't made through the automated pipeline, you may want to make rel
### Publish with a GitHub workflow
-You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/anthropics/anthropic-sdk-python/actions/workflows/publish-pypi.yml). This will require a setup organization or repository secret to be set up.
+You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/anthropics/anthropic-sdk-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up.
### Publish manually
diff --git a/README.md b/README.md
index 9a7a5662..f53dfc4f 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,6 @@ The Anthropic Python library provides convenient access to the Anthropic REST AP
application. It includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
-For the AWS Bedrock API, see [`anthropic-bedrock`](https://github.com/anthropics/anthropic-bedrock-python).
-
## Documentation
The REST API documentation can be found [on docs.anthropic.com](https://docs.anthropic.com/claude/reference/). The full API of this library can be found in [api.md](api.md).
@@ -15,6 +13,7 @@ The REST API documentation can be found [on docs.anthropic.com](https://docs.ant
## Installation
```sh
+# install from PyPI
pip install anthropic
```
@@ -36,10 +35,10 @@ message = client.messages.create(
messages=[
{
"role": "user",
- "content": "How does a court case get to the supreme court?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
print(message.content)
```
@@ -70,10 +69,10 @@ async def main() -> None:
messages=[
{
"role": "user",
- "content": "How does a court case get to the supreme court?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
print(message.content)
@@ -97,10 +96,10 @@ stream = client.messages.create(
messages=[
{
"role": "user",
- "content": "your prompt here",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
)
for event in stream:
@@ -119,10 +118,10 @@ stream = await client.messages.create(
messages=[
{
"role": "user",
- "content": "your prompt here",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
)
async for event in stream:
@@ -148,7 +147,7 @@ async def main() -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as stream:
async for text in stream.text_stream:
print(text, end="", flush=True)
@@ -191,11 +190,12 @@ For a more fully fledged example see [`examples/bedrock.py`](https://github.com/
## Token counting
-You can estimate billing for a given request with the `client.count_tokens()` method, eg:
+You can see the exact usage for a given request through the `usage` response property, e.g.
```py
-client = Anthropic()
-client.count_tokens('Hello world!') # 3
+message = client.messages.create(...)
+message.usage
+# Usage(input_tokens=25, output_tokens=13)
```
## Using types
@@ -228,10 +228,10 @@ try:
messages=[
{
"role": "user",
- "content": "your prompt here",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
except anthropic.APIConnectionError as e:
print("The server could not be reached")
@@ -280,10 +280,10 @@ client.with_options(max_retries=5).messages.create(
messages=[
{
"role": "user",
- "content": "Can you help me effectively ask for a raise at work?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
```
@@ -312,10 +312,10 @@ client.with_options(timeout=5 * 1000).messages.create(
messages=[
{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
```
@@ -375,9 +375,9 @@ response = client.messages.with_raw_response.create(
max_tokens=1024,
messages=[{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
print(response.headers.get('X-My-Header'))
@@ -408,10 +408,10 @@ with client.messages.with_streaming_response.create(
messages=[
{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as response:
print(response.headers.get("X-My-Header"))
diff --git a/api.md b/api.md
index 3c83d177..cc30d742 100644
--- a/api.md
+++ b/api.md
@@ -1,21 +1,3 @@
-# Anthropic
-
-Methods:
-
-- client.count_tokens(\*args) -> int
-
-# Completions
-
-Types:
-
-```python
-from anthropic.types import Completion
-```
-
-Methods:
-
-- client.completions.create(\*\*params) -> Completion
-
# Messages
Types:
@@ -26,6 +8,7 @@ from anthropic.types import (
ContentBlockDeltaEvent,
ContentBlockStartEvent,
ContentBlockStopEvent,
+ ImageBlockParam,
Message,
MessageDeltaEvent,
MessageDeltaUsage,
diff --git a/bin/check-release-environment b/bin/check-release-environment
index 09bb61e7..62ddc47a 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -10,9 +10,9 @@ if [ -z "${PYPI_TOKEN}" ]; then
errors+=("The ANTHROPIC_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
fi
-len=${#errors[@]}
+lenErrors=${#errors[@]}
-if [[ len -gt 0 ]]; then
+if [[ lenErrors -gt 0 ]]; then
echo -e "Found the following errors in the release environment:\n"
for error in "${errors[@]}"; do
diff --git a/examples/images.py b/examples/images.py
new file mode 100644
index 00000000..0da834bc
--- /dev/null
+++ b/examples/images.py
@@ -0,0 +1,30 @@
+from pathlib import Path
+
+from anthropic import Anthropic
+
+client = Anthropic()
+
+response = client.messages.create(
+ max_tokens=1024,
+ messages=[
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "text",
+ "text": "Hello!",
+ },
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/png",
+ "data": Path(__file__).parent.joinpath("logo.png"),
+ },
+ },
+ ],
+ },
+ ],
+ model="claude-3-opus-20240229",
+)
+print(response.model_dump_json(indent=2))
diff --git a/examples/logo.png b/examples/logo.png
new file mode 100644
index 00000000..8c299f24
Binary files /dev/null and b/examples/logo.png differ
diff --git a/examples/messages.py b/examples/messages.py
index 72fff289..f2e7e3c2 100644
--- a/examples/messages.py
+++ b/examples/messages.py
@@ -10,7 +10,7 @@
"content": "Hello!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
print(response)
@@ -30,6 +30,6 @@
"content": "How are you?",
},
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
print(response2)
diff --git a/examples/messages_stream.py b/examples/messages_stream.py
index 3c664cfd..2da36882 100644
--- a/examples/messages_stream.py
+++ b/examples/messages_stream.py
@@ -14,7 +14,7 @@ async def main() -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as stream:
async for text in stream.text_stream:
print(text, end="", flush=True)
diff --git a/examples/messages_stream_handler.py b/examples/messages_stream_handler.py
index 65e24445..27e261d0 100644
--- a/examples/messages_stream_handler.py
+++ b/examples/messages_stream_handler.py
@@ -22,7 +22,7 @@ async def main() -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
event_handler=MyStream,
) as stream:
accumulated = await stream.get_final_message()
diff --git a/examples/text_completions_demo_async.py b/examples/text_completions_demo_async.py
new file mode 100644
index 00000000..49fd5191
--- /dev/null
+++ b/examples/text_completions_demo_async.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env -S poetry run python
+
+import asyncio
+
+import anthropic
+from anthropic import AsyncAnthropic
+
+
+async def main() -> None:
+ client = AsyncAnthropic()
+
+ res = await client.completions.create(
+ model="claude-2.1",
+ prompt=f"{anthropic.HUMAN_PROMPT} how does a court case get to the Supreme Court? {anthropic.AI_PROMPT}",
+ max_tokens_to_sample=1000,
+ )
+ print(res.completion)
+
+
+asyncio.run(main())
diff --git a/examples/text_completions_demo_sync.py b/examples/text_completions_demo_sync.py
new file mode 100644
index 00000000..e386e2ad
--- /dev/null
+++ b/examples/text_completions_demo_sync.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env -S poetry run python
+
+import anthropic
+from anthropic import Anthropic
+
+
+def main() -> None:
+ client = Anthropic()
+
+ res = client.completions.create(
+ model="claude-2.1",
+ prompt=f"{anthropic.HUMAN_PROMPT} how does a court case get to the Supreme Court? {anthropic.AI_PROMPT}",
+ max_tokens_to_sample=1000,
+ )
+ print(res.completion)
+
+
+main()
diff --git a/examples/text_completions_streaming.py b/examples/text_completions_streaming.py
new file mode 100644
index 00000000..a738aad9
--- /dev/null
+++ b/examples/text_completions_streaming.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env -S poetry run python
+
+import asyncio
+
+from anthropic import AI_PROMPT, HUMAN_PROMPT, Anthropic, APIStatusError, AsyncAnthropic
+
+client = Anthropic()
+async_client = AsyncAnthropic()
+
+question = """
+Hey Claude! How can I recursively list all files in a directory in Python?
+"""
+
+
+def sync_stream() -> None:
+ stream = client.completions.create(
+ prompt=f"{HUMAN_PROMPT} {question}{AI_PROMPT}",
+ model="claude-2.1",
+ stream=True,
+ max_tokens_to_sample=300,
+ )
+
+ for completion in stream:
+ print(completion.completion, end="", flush=True)
+
+ print()
+
+
+async def async_stream() -> None:
+ stream = await async_client.completions.create(
+ prompt=f"{HUMAN_PROMPT} {question}{AI_PROMPT}",
+ model="claude-2.1",
+ stream=True,
+ max_tokens_to_sample=300,
+ )
+
+ async for completion in stream:
+ print(completion.completion, end="", flush=True)
+
+ print()
+
+
+def stream_error() -> None:
+ try:
+ client.completions.create(
+ prompt=f"{HUMAN_PROMPT} {question}{AI_PROMPT}",
+ model="claude-unknown-model",
+ stream=True,
+ max_tokens_to_sample=300,
+ )
+ except APIStatusError as err:
+ print(f"Caught API status error with response body: {err.response.text}")
+
+
+sync_stream()
+asyncio.run(async_stream())
+stream_error()
diff --git a/helpers.md b/helpers.md
index c0fecb25..ece0d550 100644
--- a/helpers.md
+++ b/helpers.md
@@ -11,7 +11,7 @@ async with client.messages.stream(
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as stream:
async for text in stream.text_stream:
print(text, end="", flush=True)
@@ -74,7 +74,7 @@ async def main() -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
event_handler=MyStream,
) as stream:
message = await stream.get_final_message()
diff --git a/pyproject.toml b/pyproject.toml
index bab96561..28716b4e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "anthropic"
-version = "0.16.0"
+version = "0.17.0"
description = "The official Python library for the anthropic API"
readme = "README.md"
license = "MIT"
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 8a9aeacd..b192ea55 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -5,69 +5,145 @@
# pre: false
# features: []
# all-features: true
+# with-sources: false
-e file:.
annotated-types==0.6.0
+ # via pydantic
anyio==4.1.0
+ # via anthropic
+ # via httpx
argcomplete==3.1.2
+ # via nox
attrs==23.1.0
+ # via pytest
boto3==1.28.58
+ # via anthropic
boto3-stubs==1.28.41
botocore==1.31.58
-botocore-stubs==1.34.31
+ # via anthropic
+ # via boto3
+ # via s3transfer
+botocore-stubs==1.34.49
+ # via boto3-stubs
cachetools==5.3.2
+ # via google-auth
certifi==2023.7.22
+ # via httpcore
+ # via httpx
+ # via requests
charset-normalizer==3.3.2
+ # via requests
colorlog==6.7.0
+ # via nox
dirty-equals==0.6.0
distlib==0.3.7
+ # via virtualenv
distro==1.8.0
+ # via anthropic
exceptiongroup==1.1.3
+ # via anyio
filelock==3.12.4
-fsspec==2023.12.2
+ # via huggingface-hub
+ # via virtualenv
+fsspec==2024.2.0
+ # via huggingface-hub
google-auth==2.26.2
+ # via anthropic
h11==0.14.0
+ # via httpcore
httpcore==1.0.2
+ # via httpx
httpx==0.25.2
+ # via anthropic
+ # via respx
huggingface-hub==0.16.4
+ # via tokenizers
idna==3.4
+ # via anyio
+ # via httpx
+ # via requests
importlib-metadata==7.0.0
iniconfig==2.0.0
+ # via pytest
jmespath==1.0.1
+ # via boto3
+ # via botocore
mypy==1.7.1
mypy-extensions==1.0.0
+ # via mypy
nodeenv==1.8.0
+ # via pyright
nox==2023.4.22
packaging==23.2
+ # via huggingface-hub
+ # via nox
+ # via pytest
platformdirs==3.11.0
+ # via virtualenv
pluggy==1.3.0
+ # via pytest
py==1.11.0
+ # via pytest
pyasn1==0.5.1
+ # via pyasn1-modules
+ # via rsa
pyasn1-modules==0.3.0
+ # via google-auth
pydantic==2.4.2
+ # via anthropic
pydantic-core==2.10.1
-pyright==1.1.332
+ # via pydantic
+pyright==1.1.351
pytest==7.1.1
+ # via pytest-asyncio
pytest-asyncio==0.21.1
python-dateutil==2.8.2
+ # via botocore
+ # via time-machine
pytz==2023.3.post1
+ # via dirty-equals
pyyaml==6.0.1
+ # via huggingface-hub
requests==2.31.0
+ # via huggingface-hub
respx==0.20.2
rsa==4.9
+ # via google-auth
ruff==0.1.9
s3transfer==0.7.0
+ # via boto3
+setuptools==68.2.2
+ # via nodeenv
six==1.16.0
+ # via python-dateutil
sniffio==1.3.0
+ # via anthropic
+ # via anyio
+ # via httpx
time-machine==2.9.0
tokenizers==0.14.0
+ # via anthropic
tomli==2.0.1
-tqdm==4.66.1
-types-awscrt==0.20.3
+ # via mypy
+ # via pytest
+tqdm==4.66.2
+ # via huggingface-hub
+types-awscrt==0.20.4
+ # via botocore-stubs
types-s3transfer==0.10.0
+ # via boto3-stubs
typing-extensions==4.8.0
+ # via anthropic
+ # via boto3-stubs
+ # via huggingface-hub
+ # via mypy
+ # via pydantic
+ # via pydantic-core
urllib3==1.26.18
+ # via botocore
+ # via requests
virtualenv==20.24.5
+ # via nox
zipp==3.17.0
-# The following packages are considered to be unsafe in a requirements file:
-setuptools==68.2.2
+ # via importlib-metadata
diff --git a/requirements.lock b/requirements.lock
index 0882deb2..c79b145e 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -5,39 +5,89 @@
# pre: false
# features: []
# all-features: true
+# with-sources: false
-e file:.
annotated-types==0.6.0
+ # via pydantic
anyio==4.1.0
-boto3==1.34.31
-botocore==1.34.31
+ # via anthropic
+ # via httpx
+boto3==1.34.45
+ # via anthropic
+botocore==1.34.45
+ # via anthropic
+ # via boto3
+ # via s3transfer
cachetools==5.3.2
+ # via google-auth
certifi==2023.7.22
+ # via httpcore
+ # via httpx
+ # via requests
charset-normalizer==3.3.2
+ # via requests
distro==1.8.0
+ # via anthropic
exceptiongroup==1.1.3
+ # via anyio
filelock==3.13.1
-fsspec==2023.12.0
-google-auth==2.26.2
+ # via huggingface-hub
+fsspec==2024.2.0
+ # via huggingface-hub
+google-auth==2.28.0
+ # via anthropic
h11==0.14.0
+ # via httpcore
httpcore==1.0.2
+ # via httpx
httpx==0.25.2
+ # via anthropic
huggingface-hub==0.16.4
+ # via tokenizers
idna==3.4
+ # via anyio
+ # via httpx
+ # via requests
jmespath==1.0.1
+ # via boto3
+ # via botocore
packaging==23.2
+ # via huggingface-hub
pyasn1==0.5.1
+ # via pyasn1-modules
+ # via rsa
pyasn1-modules==0.3.0
+ # via google-auth
pydantic==2.4.2
+ # via anthropic
pydantic-core==2.10.1
+ # via pydantic
python-dateutil==2.8.2
+ # via botocore
pyyaml==6.0.1
+ # via huggingface-hub
requests==2.31.0
+ # via huggingface-hub
rsa==4.9
+ # via google-auth
s3transfer==0.10.0
+ # via boto3
six==1.16.0
+ # via python-dateutil
sniffio==1.3.0
+ # via anthropic
+ # via anyio
+ # via httpx
tokenizers==0.14.0
-tqdm==4.66.1
+ # via anthropic
+tqdm==4.66.2
+ # via huggingface-hub
typing-extensions==4.8.0
+ # via anthropic
+ # via huggingface-hub
+ # via pydantic
+ # via pydantic-core
urllib3==1.26.18
+ # via botocore
+ # via requests
diff --git a/src/anthropic/_base_client.py b/src/anthropic/_base_client.py
index 73bd2411..dda280f6 100644
--- a/src/anthropic/_base_client.py
+++ b/src/anthropic/_base_client.py
@@ -79,7 +79,7 @@
RAW_RESPONSE_HEADER,
OVERRIDE_CAST_TO_HEADER,
)
-from ._streaming import Stream, AsyncStream
+from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder
from ._exceptions import (
APIStatusError,
APITimeoutError,
@@ -431,6 +431,9 @@ def _prepare_url(self, url: str) -> URL:
return merge_url
+ def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder:
+ return SSEDecoder()
+
def _build_request(
self,
options: FinalRequestOptions,
diff --git a/src/anthropic/_client.py b/src/anthropic/_client.py
index 4b9e2b90..7a2710f1 100644
--- a/src/anthropic/_client.py
+++ b/src/anthropic/_client.py
@@ -267,7 +267,12 @@ def count_tokens(
self,
text: str,
) -> int:
- """Count the number of tokens in a given string"""
+ """Count the number of tokens in a given string.
+
+ Note that this is only accurate for older models, e.g. `claude-2.1`. For newer
+ models this can only be used as a _very_ rough estimate, instead you should rely
+ on the `usage` property in the response for exact counts.
+ """
# Note: tokenizer is untyped
tokenizer = self.get_tokenizer()
encoded_text = tokenizer.encode(text) # type: ignore
@@ -522,7 +527,12 @@ async def count_tokens(
self,
text: str,
) -> int:
- """Count the number of tokens in a given string"""
+ """Count the number of tokens in a given string.
+
+ Note that this is only accurate for older models, e.g. `claude-2.1`. For newer
+ models this can only be used as a _very_ rough estimate, instead you should rely
+ on the `usage` property in the response for exact counts.
+ """
# Note: tokenizer is untyped
tokenizer = await self.get_tokenizer()
encoded_text = tokenizer.encode(text) # type: ignore
diff --git a/src/anthropic/_files.py b/src/anthropic/_files.py
index b6e8af8b..0d2022ae 100644
--- a/src/anthropic/_files.py
+++ b/src/anthropic/_files.py
@@ -13,12 +13,17 @@
FileContent,
RequestFiles,
HttpxFileTypes,
+ Base64FileInput,
HttpxFileContent,
HttpxRequestFiles,
)
from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
+def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
+ return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
+
+
def is_file_content(obj: object) -> TypeGuard[FileContent]:
return (
isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
diff --git a/src/anthropic/_models.py b/src/anthropic/_models.py
index 48d5624f..81089149 100644
--- a/src/anthropic/_models.py
+++ b/src/anthropic/_models.py
@@ -283,7 +283,7 @@ def construct_type(*, value: object, type_: type) -> object:
if is_union(origin):
try:
- return validate_type(type_=type_, value=value)
+ return validate_type(type_=cast("type[object]", type_), value=value)
except Exception:
pass
diff --git a/src/anthropic/_resource.py b/src/anthropic/_resource.py
index 3d719ab1..6fea098b 100644
--- a/src/anthropic/_resource.py
+++ b/src/anthropic/_resource.py
@@ -3,7 +3,8 @@
from __future__ import annotations
import time
-import asyncio
+
+import anyio
from ._base_client import SyncAPIClient, AsyncAPIClient
@@ -37,4 +38,4 @@ def __init__(self, client: AsyncAPIClient) -> None:
self._get_api_list = client.get_api_list
async def _sleep(self, seconds: float) -> None:
- await asyncio.sleep(seconds)
+ await anyio.sleep(seconds)
diff --git a/src/anthropic/_streaming.py b/src/anthropic/_streaming.py
index 80f59e3f..e267d875 100644
--- a/src/anthropic/_streaming.py
+++ b/src/anthropic/_streaming.py
@@ -5,7 +5,7 @@
import inspect
from types import TracebackType
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast
-from typing_extensions import Self, TypeGuard, override, get_origin
+from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable
import httpx
@@ -23,6 +23,8 @@ class Stream(Generic[_T]):
response: httpx.Response
+ _decoder: SSEDecoder | SSEBytesDecoder
+
def __init__(
self,
*,
@@ -33,7 +35,7 @@ def __init__(
self.response = response
self._cast_to = cast_to
self._client = client
- self._decoder = SSEDecoder()
+ self._decoder = client._make_sse_decoder()
self._iterator = self.__stream__()
def __next__(self) -> _T:
@@ -44,7 +46,10 @@ def __iter__(self) -> Iterator[_T]:
yield item
def _iter_events(self) -> Iterator[ServerSentEvent]:
- yield from self._decoder.iter(self.response.iter_lines())
+ if isinstance(self._decoder, SSEBytesDecoder):
+ yield from self._decoder.iter_bytes(self.response.iter_bytes())
+ else:
+ yield from self._decoder.iter(self.response.iter_lines())
def __stream__(self) -> Iterator[_T]:
cast_to = cast(Any, self._cast_to)
@@ -117,6 +122,8 @@ class AsyncStream(Generic[_T]):
response: httpx.Response
+ _decoder: SSEDecoder | SSEBytesDecoder
+
def __init__(
self,
*,
@@ -127,7 +134,7 @@ def __init__(
self.response = response
self._cast_to = cast_to
self._client = client
- self._decoder = SSEDecoder()
+ self._decoder = client._make_sse_decoder()
self._iterator = self.__stream__()
async def __anext__(self) -> _T:
@@ -138,8 +145,12 @@ async def __aiter__(self) -> AsyncIterator[_T]:
yield item
async def _iter_events(self) -> AsyncIterator[ServerSentEvent]:
- async for sse in self._decoder.aiter(self.response.aiter_lines()):
- yield sse
+ if isinstance(self._decoder, SSEBytesDecoder):
+ async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()):
+ yield sse
+ else:
+ async for sse in self._decoder.aiter(self.response.aiter_lines()):
+ yield sse
async def __stream__(self) -> AsyncIterator[_T]:
cast_to = cast(Any, self._cast_to)
@@ -325,6 +336,17 @@ def decode(self, line: str) -> ServerSentEvent | None:
return None
+@runtime_checkable
+class SSEBytesDecoder(Protocol):
+ def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]:
+ """Given an iterator that yields raw binary data, iterate over it & yield every event encountered"""
+ ...
+
+ def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]:
+ """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered"""
+ ...
+
+
def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]:
"""TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`"""
origin = get_origin(typ) or typ
diff --git a/src/anthropic/_types.py b/src/anthropic/_types.py
index a67d613b..6e8fde5a 100644
--- a/src/anthropic/_types.py
+++ b/src/anthropic/_types.py
@@ -41,8 +41,10 @@
ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]]
ProxiesTypes = Union[str, Proxy, ProxiesDict]
if TYPE_CHECKING:
+ Base64FileInput = Union[IO[bytes], PathLike[str]]
FileContent = Union[IO[bytes], bytes, PathLike[str]]
else:
+ Base64FileInput = Union[IO[bytes], PathLike]
FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8.
FileTypes = Union[
# file (or bytes)
diff --git a/src/anthropic/_utils/__init__.py b/src/anthropic/_utils/__init__.py
index b5790a87..56978941 100644
--- a/src/anthropic/_utils/__init__.py
+++ b/src/anthropic/_utils/__init__.py
@@ -44,5 +44,7 @@
from ._transform import (
PropertyInfo as PropertyInfo,
transform as transform,
+ async_transform as async_transform,
maybe_transform as maybe_transform,
+ async_maybe_transform as async_maybe_transform,
)
diff --git a/src/anthropic/_utils/_proxy.py b/src/anthropic/_utils/_proxy.py
index 6f05efcd..b9c12dc3 100644
--- a/src/anthropic/_utils/_proxy.py
+++ b/src/anthropic/_utils/_proxy.py
@@ -45,7 +45,7 @@ def __dir__(self) -> Iterable[str]:
@property # type: ignore
@override
- def __class__(self) -> type:
+ def __class__(self) -> type: # pyright: ignore
proxied = self.__get_proxied__()
if issubclass(type(proxied), LazyProxy):
return type(proxied)
diff --git a/src/anthropic/_utils/_transform.py b/src/anthropic/_utils/_transform.py
index 2cb7726c..1bd1330c 100644
--- a/src/anthropic/_utils/_transform.py
+++ b/src/anthropic/_utils/_transform.py
@@ -1,9 +1,13 @@
from __future__ import annotations
+import io
+import base64
+import pathlib
from typing import Any, Mapping, TypeVar, cast
from datetime import date, datetime
from typing_extensions import Literal, get_args, override, get_type_hints
+import anyio
import pydantic
from ._utils import (
@@ -11,6 +15,7 @@
is_mapping,
is_iterable,
)
+from .._files import is_base64_file_input
from ._typing import (
is_list_type,
is_union_type,
@@ -29,7 +34,7 @@
# TODO: ensure works correctly with forward references in all cases
-PropertyFormat = Literal["iso8601", "custom"]
+PropertyFormat = Literal["iso8601", "base64", "custom"]
class PropertyInfo:
@@ -180,11 +185,7 @@ def _transform_recursive(
if isinstance(data, pydantic.BaseModel):
return model_dump(data, exclude_unset=True)
- return _transform_value(data, annotation)
-
-
-def _transform_value(data: object, type_: type) -> object:
- annotated_type = _get_annotated_type(type_)
+ annotated_type = _get_annotated_type(annotation)
if annotated_type is None:
return data
@@ -205,6 +206,22 @@ def _format_data(data: object, format_: PropertyFormat, format_template: str | N
if format_ == "custom" and format_template is not None:
return data.strftime(format_template)
+ if format_ == "base64" and is_base64_file_input(data):
+ binary: str | bytes | None = None
+
+ if isinstance(data, pathlib.Path):
+ binary = data.read_bytes()
+ elif isinstance(data, io.IOBase):
+ binary = data.read()
+
+ if isinstance(binary, str): # type: ignore[unreachable]
+ binary = binary.encode()
+
+ if not isinstance(binary, bytes):
+ raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}")
+
+ return base64.b64encode(binary).decode("ascii")
+
return data
@@ -222,3 +239,141 @@ def _transform_typeddict(
else:
result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_)
return result
+
+
+async def async_maybe_transform(
+ data: object,
+ expected_type: object,
+) -> Any | None:
+ """Wrapper over `async_transform()` that allows `None` to be passed.
+
+ See `async_transform()` for more details.
+ """
+ if data is None:
+ return None
+ return await async_transform(data, expected_type)
+
+
+async def async_transform(
+ data: _T,
+ expected_type: object,
+) -> _T:
+ """Transform dictionaries based off of type information from the given type, for example:
+
+ ```py
+ class Params(TypedDict, total=False):
+ card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]]
+
+
+ transformed = transform({"card_id": ""}, Params)
+ # {'cardID': ''}
+ ```
+
+ Any keys / data that does not have type information given will be included as is.
+
+ It should be noted that the transformations that this function does are not represented in the type system.
+ """
+ transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type))
+ return cast(_T, transformed)
+
+
+async def _async_transform_recursive(
+ data: object,
+ *,
+ annotation: type,
+ inner_type: type | None = None,
+) -> object:
+ """Transform the given data against the expected type.
+
+ Args:
+ annotation: The direct type annotation given to the particular piece of data.
+ This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc
+
+ inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type
+ is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in
+ the list can be transformed using the metadata from the container type.
+
+ Defaults to the same value as the `annotation` argument.
+ """
+ if inner_type is None:
+ inner_type = annotation
+
+ stripped_type = strip_annotated_type(inner_type)
+ if is_typeddict(stripped_type) and is_mapping(data):
+ return await _async_transform_typeddict(data, stripped_type)
+
+ if (
+ # List[T]
+ (is_list_type(stripped_type) and is_list(data))
+ # Iterable[T]
+ or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
+ ):
+ inner_type = extract_type_arg(stripped_type, 0)
+ return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
+
+ if is_union_type(stripped_type):
+ # For union types we run the transformation against all subtypes to ensure that everything is transformed.
+ #
+ # TODO: there may be edge cases where the same normalized field name will transform to two different names
+ # in different subtypes.
+ for subtype in get_args(stripped_type):
+ data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype)
+ return data
+
+ if isinstance(data, pydantic.BaseModel):
+ return model_dump(data, exclude_unset=True)
+
+ annotated_type = _get_annotated_type(annotation)
+ if annotated_type is None:
+ return data
+
+ # ignore the first argument as it is the actual type
+ annotations = get_args(annotated_type)[1:]
+ for annotation in annotations:
+ if isinstance(annotation, PropertyInfo) and annotation.format is not None:
+ return await _async_format_data(data, annotation.format, annotation.format_template)
+
+ return data
+
+
+async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object:
+ if isinstance(data, (date, datetime)):
+ if format_ == "iso8601":
+ return data.isoformat()
+
+ if format_ == "custom" and format_template is not None:
+ return data.strftime(format_template)
+
+ if format_ == "base64" and is_base64_file_input(data):
+ binary: str | bytes | None = None
+
+ if isinstance(data, pathlib.Path):
+ binary = await anyio.Path(data).read_bytes()
+ elif isinstance(data, io.IOBase):
+ binary = data.read()
+
+ if isinstance(binary, str): # type: ignore[unreachable]
+ binary = binary.encode()
+
+ if not isinstance(binary, bytes):
+ raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}")
+
+ return base64.b64encode(binary).decode("ascii")
+
+ return data
+
+
+async def _async_transform_typeddict(
+ data: Mapping[str, object],
+ expected_type: type,
+) -> Mapping[str, object]:
+ result: dict[str, object] = {}
+ annotations = get_type_hints(expected_type, include_extras=True)
+ for key, value in data.items():
+ type_ = annotations.get(key)
+ if type_ is None:
+ # we do not have a type annotation for this field, leave it as is
+ result[key] = value
+ else:
+ result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)
+ return result
diff --git a/src/anthropic/_version.py b/src/anthropic/_version.py
index 6cd53eaf..c4ae44c9 100644
--- a/src/anthropic/_version.py
+++ b/src/anthropic/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless.
__title__ = "anthropic"
-__version__ = "0.16.0" # x-release-please-version
+__version__ = "0.17.0" # x-release-please-version
diff --git a/src/anthropic/lib/bedrock/_auth.py b/src/anthropic/lib/bedrock/_auth.py
index 503094ea..df3b5f6b 100644
--- a/src/anthropic/lib/bedrock/_auth.py
+++ b/src/anthropic/lib/bedrock/_auth.py
@@ -39,4 +39,4 @@ def get_auth_headers(
prepped = request.prepare()
- return dict(prepped.headers)
+ return {key: value for key, value in dict(prepped.headers).items() if value is not None}
diff --git a/src/anthropic/lib/bedrock/_client.py b/src/anthropic/lib/bedrock/_client.py
index 9e4e8318..faf16458 100644
--- a/src/anthropic/lib/bedrock/_client.py
+++ b/src/anthropic/lib/bedrock/_client.py
@@ -2,19 +2,18 @@
import os
from typing import Any, Union, Mapping, TypeVar
-from typing_extensions import override, get_origin
+from typing_extensions import override
import httpx
from ... import _exceptions
-from ._stream import BedrockStream, AsyncBedrockStream
-from ..._types import NOT_GIVEN, NotGiven, ResponseT
+from ..._types import NOT_GIVEN, NotGiven
from ..._utils import is_dict
from ..._version import __version__
-from ..._response import extract_stream_chunk_type
from ..._streaming import Stream, AsyncStream
from ..._exceptions import APIStatusError
from ..._base_client import DEFAULT_MAX_RETRIES, BaseClient, SyncAPIClient, AsyncAPIClient, FinalRequestOptions
+from ._stream_decoder import AWSEventStreamDecoder
from ...resources.completions import Completions, AsyncCompletions
DEFAULT_VERSION = "bedrock-2023-05-31"
@@ -131,10 +130,12 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- self._default_stream_cls = BedrockStream
-
self.completions = Completions(self)
+ @override
+ def _make_sse_decoder(self) -> AWSEventStreamDecoder:
+ return AWSEventStreamDecoder()
+
@override
def _prepare_request(self, request: httpx.Request) -> None:
from ._auth import get_auth_headers
@@ -153,31 +154,6 @@ def _prepare_request(self, request: httpx.Request) -> None:
)
request.headers.update(headers)
- @override
- def _process_response(
- self,
- *,
- cast_to: type[ResponseT],
- options: FinalRequestOptions,
- response: httpx.Response,
- stream: bool,
- stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
- ) -> ResponseT:
- if stream_cls is not None and get_origin(stream_cls) == Stream:
- chunk_type = extract_stream_chunk_type(stream_cls)
-
- # the type: ignore is required as mypy doesn't like us
- # dynamically created a concrete type like this
- stream_cls = BedrockStream[chunk_type] # type: ignore
-
- return super()._process_response(
- cast_to=cast_to,
- options=options,
- response=response,
- stream=stream,
- stream_cls=stream_cls,
- )
-
class AsyncAnthropicBedrock(BaseBedrockClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient):
completions: AsyncCompletions
@@ -231,10 +207,12 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- self._default_stream_cls = AsyncBedrockStream
-
self.completions = AsyncCompletions(self)
+ @override
+ def _make_sse_decoder(self) -> AWSEventStreamDecoder:
+ return AWSEventStreamDecoder()
+
@override
async def _prepare_request(self, request: httpx.Request) -> None:
from ._auth import get_auth_headers
@@ -252,28 +230,3 @@ async def _prepare_request(self, request: httpx.Request) -> None:
data=data,
)
request.headers.update(headers)
-
- @override
- async def _process_response(
- self,
- *,
- cast_to: type[ResponseT],
- options: FinalRequestOptions,
- response: httpx.Response,
- stream: bool,
- stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
- ) -> ResponseT:
- if stream_cls is not None and get_origin(stream_cls) == AsyncStream:
- chunk_type = extract_stream_chunk_type(stream_cls)
-
- # the type: ignore is required as mypy doesn't like us
- # dynamically created a concrete type like this
- stream_cls = AsyncBedrockStream[chunk_type] # type: ignore
-
- return await super()._process_response(
- cast_to=cast_to,
- options=options,
- response=response,
- stream=stream,
- stream_cls=stream_cls,
- )
diff --git a/src/anthropic/lib/bedrock/_stream.py b/src/anthropic/lib/bedrock/_stream.py
index 2140acdc..6512c468 100644
--- a/src/anthropic/lib/bedrock/_stream.py
+++ b/src/anthropic/lib/bedrock/_stream.py
@@ -1,21 +1,17 @@
from __future__ import annotations
-from typing import TypeVar, Iterator
-from typing_extensions import AsyncIterator, override
+from typing import TypeVar
import httpx
from ..._client import Anthropic, AsyncAnthropic
-from ..._streaming import Stream, AsyncStream, ServerSentEvent
+from ..._streaming import Stream, AsyncStream
from ._stream_decoder import AWSEventStreamDecoder
_T = TypeVar("_T")
class BedrockStream(Stream[_T]):
- # the AWS decoder expects `bytes` instead of `str`
- _decoder: AWSEventStreamDecoder # type: ignore
-
def __init__(
self,
*,
@@ -27,15 +23,8 @@ def __init__(
self._decoder = AWSEventStreamDecoder()
- @override
- def _iter_events(self) -> Iterator[ServerSentEvent]:
- yield from self._decoder.iter(self.response.iter_bytes())
-
class AsyncBedrockStream(AsyncStream[_T]):
- # the AWS decoder expects `bytes` instead of `str`
- _decoder: AWSEventStreamDecoder # type: ignore
-
def __init__(
self,
*,
@@ -46,8 +35,3 @@ def __init__(
super().__init__(cast_to=cast_to, response=response, client=client)
self._decoder = AWSEventStreamDecoder()
-
- @override
- async def _iter_events(self) -> AsyncIterator[ServerSentEvent]:
- async for sse in self._decoder.aiter(self.response.aiter_bytes()):
- yield sse
diff --git a/src/anthropic/lib/bedrock/_stream_decoder.py b/src/anthropic/lib/bedrock/_stream_decoder.py
index a99647d7..2065de4b 100644
--- a/src/anthropic/lib/bedrock/_stream_decoder.py
+++ b/src/anthropic/lib/bedrock/_stream_decoder.py
@@ -27,7 +27,7 @@ def __init__(self) -> None:
self.parser = EventStreamJSONParser()
- def iter(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]:
+ def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]:
"""Given an iterator that yields lines, iterate over it & yield every event encountered"""
from botocore.eventstream import EventStreamBuffer
@@ -39,7 +39,7 @@ def iter(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]:
if message:
yield ServerSentEvent(data=message, event="completion")
- async def aiter(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]:
+ async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]:
"""Given an async iterator that yields lines, iterate over it & yield every event encountered"""
from botocore.eventstream import EventStreamBuffer
diff --git a/src/anthropic/lib/streaming/_messages.py b/src/anthropic/lib/streaming/_messages.py
index 2f4bc7e5..02dc62f7 100644
--- a/src/anthropic/lib/streaming/_messages.py
+++ b/src/anthropic/lib/streaming/_messages.py
@@ -421,7 +421,8 @@ def accumulate_event(*, event: MessageStreamEvent, current_snapshot: Message | N
)
elif event.type == "content_block_delta":
content = current_snapshot.content[event.index]
- content.text += event.delta.text
+ if content.type == "text" and event.delta.type == "text_delta":
+ content.text += event.delta.text
elif event.type == "message_delta":
current_snapshot.stop_reason = event.delta.stop_reason
current_snapshot.stop_sequence = event.delta.stop_sequence
diff --git a/src/anthropic/resources/completions.py b/src/anthropic/resources/completions.py
index d672336f..c75f858f 100644
--- a/src/anthropic/resources/completions.py
+++ b/src/anthropic/resources/completions.py
@@ -10,7 +10,11 @@
from .. import _legacy_response
from ..types import Completion, completion_create_params
from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._utils import required_args, maybe_transform
+from .._utils import (
+ required_args,
+ maybe_transform,
+ 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
@@ -36,7 +40,7 @@ def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
stop_sequences: List[str] | NotGiven = NOT_GIVEN,
@@ -51,8 +55,17 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = 600,
) -> Completion:
- """
- Create a Text Completion
+ """[Legacy] Create a Text Completion.
+
+ The Text Completions API is a legacy API.
+
+ We recommend using the
+ [Messages API](https://docs.anthropic.com/claude/reference/messages_post) going
+ forward.
+
+ Future models and features will not be compatible with Text Completions. See our
+ [migration guide](https://docs.anthropic.com/claude/reference/migrating-from-text-completions-to-messages)
+ for guidance in migrating from Text Completions to Messages.
Args:
max_tokens_to_sample: The maximum number of tokens to generate before stopping.
@@ -62,13 +75,7 @@ def create(
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
prompt: The prompt that you want Claude to complete.
@@ -102,14 +109,21 @@ def create(
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -117,6 +131,9 @@ def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -132,7 +149,7 @@ def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
stream: Literal[True],
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
@@ -147,8 +164,17 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = 600,
) -> Stream[Completion]:
- """
- Create a Text Completion
+ """[Legacy] Create a Text Completion.
+
+ The Text Completions API is a legacy API.
+
+ We recommend using the
+ [Messages API](https://docs.anthropic.com/claude/reference/messages_post) going
+ forward.
+
+ Future models and features will not be compatible with Text Completions. See our
+ [migration guide](https://docs.anthropic.com/claude/reference/migrating-from-text-completions-to-messages)
+ for guidance in migrating from Text Completions to Messages.
Args:
max_tokens_to_sample: The maximum number of tokens to generate before stopping.
@@ -158,13 +184,7 @@ def create(
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
prompt: The prompt that you want Claude to complete.
@@ -198,14 +218,21 @@ def create(
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -213,6 +240,9 @@ def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -228,7 +258,7 @@ def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
stream: bool,
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
@@ -243,8 +273,17 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = 600,
) -> Completion | Stream[Completion]:
- """
- Create a Text Completion
+ """[Legacy] Create a Text Completion.
+
+ The Text Completions API is a legacy API.
+
+ We recommend using the
+ [Messages API](https://docs.anthropic.com/claude/reference/messages_post) going
+ forward.
+
+ Future models and features will not be compatible with Text Completions. See our
+ [migration guide](https://docs.anthropic.com/claude/reference/migrating-from-text-completions-to-messages)
+ for guidance in migrating from Text Completions to Messages.
Args:
max_tokens_to_sample: The maximum number of tokens to generate before stopping.
@@ -254,13 +293,7 @@ def create(
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
prompt: The prompt that you want Claude to complete.
@@ -294,14 +327,21 @@ def create(
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -309,6 +349,9 @@ def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -324,7 +367,7 @@ def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
stop_sequences: List[str] | NotGiven = NOT_GIVEN,
@@ -378,7 +421,7 @@ async def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
stop_sequences: List[str] | NotGiven = NOT_GIVEN,
@@ -393,8 +436,17 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = 600,
) -> Completion:
- """
- Create a Text Completion
+ """[Legacy] Create a Text Completion.
+
+ The Text Completions API is a legacy API.
+
+ We recommend using the
+ [Messages API](https://docs.anthropic.com/claude/reference/messages_post) going
+ forward.
+
+ Future models and features will not be compatible with Text Completions. See our
+ [migration guide](https://docs.anthropic.com/claude/reference/migrating-from-text-completions-to-messages)
+ for guidance in migrating from Text Completions to Messages.
Args:
max_tokens_to_sample: The maximum number of tokens to generate before stopping.
@@ -404,13 +456,7 @@ async def create(
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
prompt: The prompt that you want Claude to complete.
@@ -444,14 +490,21 @@ async def create(
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -459,6 +512,9 @@ async def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -474,7 +530,7 @@ async def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
stream: Literal[True],
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
@@ -489,8 +545,17 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = 600,
) -> AsyncStream[Completion]:
- """
- Create a Text Completion
+ """[Legacy] Create a Text Completion.
+
+ The Text Completions API is a legacy API.
+
+ We recommend using the
+ [Messages API](https://docs.anthropic.com/claude/reference/messages_post) going
+ forward.
+
+ Future models and features will not be compatible with Text Completions. See our
+ [migration guide](https://docs.anthropic.com/claude/reference/migrating-from-text-completions-to-messages)
+ for guidance in migrating from Text Completions to Messages.
Args:
max_tokens_to_sample: The maximum number of tokens to generate before stopping.
@@ -500,13 +565,7 @@ async def create(
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
prompt: The prompt that you want Claude to complete.
@@ -540,14 +599,21 @@ async def create(
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -555,6 +621,9 @@ async def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -570,7 +639,7 @@ async def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
stream: bool,
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
@@ -585,8 +654,17 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = 600,
) -> Completion | AsyncStream[Completion]:
- """
- Create a Text Completion
+ """[Legacy] Create a Text Completion.
+
+ The Text Completions API is a legacy API.
+
+ We recommend using the
+ [Messages API](https://docs.anthropic.com/claude/reference/messages_post) going
+ forward.
+
+ Future models and features will not be compatible with Text Completions. See our
+ [migration guide](https://docs.anthropic.com/claude/reference/migrating-from-text-completions-to-messages)
+ for guidance in migrating from Text Completions to Messages.
Args:
max_tokens_to_sample: The maximum number of tokens to generate before stopping.
@@ -596,13 +674,7 @@ async def create(
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
prompt: The prompt that you want Claude to complete.
@@ -636,14 +708,21 @@ async def create(
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -651,6 +730,9 @@ async def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -666,7 +748,7 @@ async def create(
self,
*,
max_tokens_to_sample: int,
- model: Union[str, Literal["claude-2.1", "claude-instant-1"]],
+ model: Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]],
prompt: str,
metadata: completion_create_params.Metadata | NotGiven = NOT_GIVEN,
stop_sequences: List[str] | NotGiven = NOT_GIVEN,
@@ -683,7 +765,7 @@ async def create(
) -> Completion | AsyncStream[Completion]:
return await self._post(
"/v1/complete",
- body=maybe_transform(
+ body=await async_maybe_transform(
{
"max_tokens_to_sample": max_tokens_to_sample,
"model": model,
diff --git a/src/anthropic/resources/messages.py b/src/anthropic/resources/messages.py
index ad908c02..17f968d1 100644
--- a/src/anthropic/resources/messages.py
+++ b/src/anthropic/resources/messages.py
@@ -11,7 +11,11 @@
from .. import _legacy_response
from ..types import Message, MessageParam, MessageStreamEvent, message_create_params
from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._utils import required_args, maybe_transform
+from .._utils import (
+ required_args,
+ maybe_transform,
+ 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
@@ -64,11 +68,11 @@ def create(
"""
Create a Message.
- Send a structured list of input messages, and the model will generate the next
- message in the conversation.
+ Send a structured list of input messages with text and/or image content, and the
+ model will generate the next message in the conversation.
- Messages can be used for either single queries to the model or for multi-turn
- conversations.
+ The Messages API can be used for for either single queries or stateless
+ multi-turn conversations.
Args:
max_tokens: The maximum number of tokens to generate before stopping.
@@ -77,8 +81,7 @@ def create(
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
messages: Input messages.
@@ -115,15 +118,18 @@ def create(
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -133,24 +139,39 @@ def create(
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
metadata: An object describing metadata about the request.
@@ -174,18 +195,25 @@ def create(
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -193,6 +221,9 @@ def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -227,11 +258,11 @@ def create(
"""
Create a Message.
- Send a structured list of input messages, and the model will generate the next
- message in the conversation.
+ Send a structured list of input messages with text and/or image content, and the
+ model will generate the next message in the conversation.
- Messages can be used for either single queries to the model or for multi-turn
- conversations.
+ The Messages API can be used for for either single queries or stateless
+ multi-turn conversations.
Args:
max_tokens: The maximum number of tokens to generate before stopping.
@@ -240,8 +271,7 @@ def create(
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
messages: Input messages.
@@ -278,15 +308,18 @@ def create(
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -296,24 +329,39 @@ def create(
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
stream: Whether to incrementally stream the response using server-sent events.
@@ -337,18 +385,25 @@ def create(
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -356,6 +411,9 @@ def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -390,11 +448,11 @@ def create(
"""
Create a Message.
- Send a structured list of input messages, and the model will generate the next
- message in the conversation.
+ Send a structured list of input messages with text and/or image content, and the
+ model will generate the next message in the conversation.
- Messages can be used for either single queries to the model or for multi-turn
- conversations.
+ The Messages API can be used for for either single queries or stateless
+ multi-turn conversations.
Args:
max_tokens: The maximum number of tokens to generate before stopping.
@@ -403,8 +461,7 @@ def create(
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
messages: Input messages.
@@ -441,15 +498,18 @@ def create(
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -459,24 +519,39 @@ def create(
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
stream: Whether to incrementally stream the response using server-sent events.
@@ -500,18 +575,25 @@ def create(
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -519,6 +601,9 @@ def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -709,11 +794,11 @@ async def create(
"""
Create a Message.
- Send a structured list of input messages, and the model will generate the next
- message in the conversation.
+ Send a structured list of input messages with text and/or image content, and the
+ model will generate the next message in the conversation.
- Messages can be used for either single queries to the model or for multi-turn
- conversations.
+ The Messages API can be used for for either single queries or stateless
+ multi-turn conversations.
Args:
max_tokens: The maximum number of tokens to generate before stopping.
@@ -722,8 +807,7 @@ async def create(
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
messages: Input messages.
@@ -760,15 +844,18 @@ async def create(
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -778,24 +865,39 @@ async def create(
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
metadata: An object describing metadata about the request.
@@ -819,18 +921,25 @@ async def create(
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -838,6 +947,9 @@ async def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -872,11 +984,11 @@ async def create(
"""
Create a Message.
- Send a structured list of input messages, and the model will generate the next
- message in the conversation.
+ Send a structured list of input messages with text and/or image content, and the
+ model will generate the next message in the conversation.
- Messages can be used for either single queries to the model or for multi-turn
- conversations.
+ The Messages API can be used for for either single queries or stateless
+ multi-turn conversations.
Args:
max_tokens: The maximum number of tokens to generate before stopping.
@@ -885,8 +997,7 @@ async def create(
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
messages: Input messages.
@@ -923,15 +1034,18 @@ async def create(
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -941,24 +1055,39 @@ async def create(
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
stream: Whether to incrementally stream the response using server-sent events.
@@ -982,18 +1111,25 @@ async def create(
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -1001,6 +1137,9 @@ async def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -1035,11 +1174,11 @@ async def create(
"""
Create a Message.
- Send a structured list of input messages, and the model will generate the next
- message in the conversation.
+ Send a structured list of input messages with text and/or image content, and the
+ model will generate the next message in the conversation.
- Messages can be used for either single queries to the model or for multi-turn
- conversations.
+ The Messages API can be used for for either single queries or stateless
+ multi-turn conversations.
Args:
max_tokens: The maximum number of tokens to generate before stopping.
@@ -1048,8 +1187,7 @@ async def create(
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
messages: Input messages.
@@ -1086,15 +1224,18 @@ async def create(
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -1104,24 +1245,39 @@ async def create(
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
model: The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
stream: Whether to incrementally stream the response using server-sent events.
@@ -1145,18 +1301,25 @@ async def create(
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
temperature: Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
top_k: Only sample from the top K options for each subsequent token.
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
top_p: Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options
@@ -1164,6 +1327,9 @@ async def create(
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -1197,7 +1363,7 @@ async def create(
) -> Message | AsyncStream[MessageStreamEvent]:
return await self._post(
"/v1/messages",
- body=maybe_transform(
+ body=await async_maybe_transform(
{
"max_tokens": max_tokens,
"messages": messages,
diff --git a/src/anthropic/types/__init__.py b/src/anthropic/types/__init__.py
index 42df2f09..c576eaf9 100644
--- a/src/anthropic/types/__init__.py
+++ b/src/anthropic/types/__init__.py
@@ -9,6 +9,7 @@
from .content_block import ContentBlock as ContentBlock
from .message_param import MessageParam as MessageParam
from .text_block_param import TextBlockParam as TextBlockParam
+from .image_block_param import ImageBlockParam as ImageBlockParam
from .message_stop_event import MessageStopEvent as MessageStopEvent
from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent
from .message_delta_usage import MessageDeltaUsage as MessageDeltaUsage
diff --git a/src/anthropic/types/completion.py b/src/anthropic/types/completion.py
index d4bacc2f..ed2d6fdb 100644
--- a/src/anthropic/types/completion.py
+++ b/src/anthropic/types/completion.py
@@ -32,3 +32,7 @@ class Completion(BaseModel):
"""
type: Literal["completion"]
+ """Object type.
+
+ For Text Completions, this is always `"completion"`.
+ """
diff --git a/src/anthropic/types/completion_create_params.py b/src/anthropic/types/completion_create_params.py
index ff367899..52d0d41d 100644
--- a/src/anthropic/types/completion_create_params.py
+++ b/src/anthropic/types/completion_create_params.py
@@ -25,16 +25,10 @@ class CompletionCreateParamsBase(TypedDict, total=False):
only specifies the absolute maximum number of tokens to generate.
"""
- model: Required[Union[str, Literal["claude-2.1", "claude-instant-1"]]]
+ model: Required[Union[str, Literal["claude-3-opus-20240229", "claude-2.1", "claude-instant-1"]]]
"""The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
"""
@@ -69,8 +63,12 @@ class CompletionCreateParamsBase(TypedDict, total=False):
temperature: float
"""Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
"""
top_k: int
@@ -78,6 +76,9 @@ class CompletionCreateParamsBase(TypedDict, total=False):
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
"""
top_p: float
@@ -87,6 +88,9 @@ class CompletionCreateParamsBase(TypedDict, total=False):
for each subsequent token in decreasing probability order and cut it off once it
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
"""
diff --git a/src/anthropic/types/image_block_param.py b/src/anthropic/types/image_block_param.py
new file mode 100644
index 00000000..c0788c85
--- /dev/null
+++ b/src/anthropic/types/image_block_param.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._types import Base64FileInput
+from .._utils import PropertyInfo
+
+__all__ = ["ImageBlockParam", "Source"]
+
+
+class Source(TypedDict, total=False):
+ data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]]
+
+ media_type: Required[Literal["image/jpeg", "image/png", "image/gif", "image/webp"]]
+
+ type: Literal["base64"]
+
+
+class ImageBlockParam(TypedDict, total=False):
+ source: Required[Source]
+
+ type: Literal["image"]
diff --git a/src/anthropic/types/message.py b/src/anthropic/types/message.py
index 005eb29b..3bbd7afa 100644
--- a/src/anthropic/types/message.py
+++ b/src/anthropic/types/message.py
@@ -21,7 +21,7 @@ class Message(BaseModel):
"""Content generated by the model.
This is an array of content blocks, each of which has a `type` that determines
- its shape. Currently, the only `type` available is `"text"`.
+ its shape. Currently, the only `type` in responses is `"text"`.
Example:
@@ -41,10 +41,7 @@ class Message(BaseModel):
"role": "user",
"content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
},
- {
- "role": "assistant",
- "content": "The best answer is ("
- }
+ { "role": "assistant", "content": "The best answer is (" }
]
```
@@ -81,12 +78,29 @@ class Message(BaseModel):
"""
stop_sequence: Optional[str] = None
- """Which custom stop sequence was generated.
+ """Which custom stop sequence was generated, if any.
- This value will be non-null if one of your custom stop sequences was generated.
+ This value will be a non-null string if one of your custom stop sequences was
+ generated.
"""
type: Literal["message"]
+ """Object type.
+
+ For Messages, this is always `"message"`.
+ """
usage: Usage
- """Container for the number of tokens used."""
+ """Billing and rate-limit usage.
+
+ Anthropic's API bills and rate-limits by token counts, as tokens represent the
+ underlying cost to our systems.
+
+ Under the hood, the API transforms requests into a format suitable for the
+ model. The model's output then goes through a parsing stage before becoming an
+ API response. As a result, the token counts in `usage` will not match one-to-one
+ with the exact visible content of an API request or response.
+
+ For example, `output_tokens` will be non-zero, even for an empty string response
+ from Claude.
+ """
diff --git a/src/anthropic/types/message_create_params.py b/src/anthropic/types/message_create_params.py
index 21d56395..3c3c7b3f 100644
--- a/src/anthropic/types/message_create_params.py
+++ b/src/anthropic/types/message_create_params.py
@@ -18,8 +18,7 @@ class MessageCreateParamsBase(TypedDict, total=False):
only specifies the absolute maximum number of tokens to generate.
Different models have different maximum values for this parameter. See
- [input and output sizes](https://docs.anthropic.com/claude/reference/input-and-output-sizes)
- for details.
+ [models](https://docs.anthropic.com/claude/docs/models-overview) for details.
"""
messages: Required[Iterable[MessageParam]]
@@ -58,15 +57,18 @@ class MessageCreateParamsBase(TypedDict, total=False):
```json
[
- { "role": "user", "content": "Please describe yourself using only JSON" },
- { "role": "assistant", "content": "Here is my JSON description:\n{" }
+ {
+ "role": "user",
+ "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"
+ },
+ { "role": "assistant", "content": "The best answer is (" }
]
```
Each input message `content` may be either a single `string` or an array of
- content blocks, where each block has a specific `type`. Using a `string` is
- shorthand for an array of one content block of type `"text"`. The following
- input messages are equivalent:
+ content blocks, where each block has a specific `type`. Using a `string` for
+ `content` is shorthand for an array of one content block of type `"text"`. The
+ following input messages are equivalent:
```json
{ "role": "user", "content": "Hello, Claude" }
@@ -76,26 +78,41 @@ class MessageCreateParamsBase(TypedDict, total=False):
{ "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] }
```
- See our
- [guide to prompt design](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design)
- for more details on how to best construct prompts.
+ Starting with Claude 3 models, you can also send image content blocks:
+
+ ```json
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image",
+ "source": {
+ "type": "base64",
+ "media_type": "image/jpeg",
+ "data": "/9j/4AAQSkZJRg..."
+ }
+ },
+ { "type": "text", "text": "What is in this image?" }
+ ]
+ }
+ ```
+
+ We currently support the `base64` source type for images, and the `image/jpeg`,
+ `image/png`, `image/gif`, and `image/webp` media types.
+
+ See [examples](https://docs.anthropic.com/claude/reference/messages-examples)
+ for more input examples.
Note that if you want to include a
- [system prompt](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts),
- you can use the top-level `system` parameter — there is no `"system"` role for
- input messages in the Messages API.
+ [system prompt](https://docs.anthropic.com/claude/docs/system-prompts), you can
+ use the top-level `system` parameter — there is no `"system"` role for input
+ messages in the Messages API.
"""
model: Required[str]
"""The model that will complete your prompt.
- As we improve Claude, we develop new versions of it that you can query. The
- `model` parameter controls which version of Claude responds to your request.
- Right now we offer two model families: Claude, and Claude Instant. You can use
- them by setting `model` to `"claude-2.1"` or `"claude-instant-1.2"`,
- respectively.
-
- See [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
+ See [models](https://docs.anthropic.com/claude/docs/models-overview) for
additional details and options.
"""
@@ -119,14 +136,18 @@ class MessageCreateParamsBase(TypedDict, total=False):
A system prompt is a way of providing context and instructions to Claude, such
as specifying a particular goal or role. See our
- [guide to system prompts](https://docs.anthropic.com/claude/docs/how-to-use-system-prompts).
+ [guide to system prompts](https://docs.anthropic.com/claude/docs/system-prompts).
"""
temperature: float
"""Amount of randomness injected into the response.
- Defaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical /
- multiple choice, and closer to 1 for creative and generative tasks.
+ Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0`
+ for analytical / multiple choice, and closer to `1.0` for creative and
+ generative tasks.
+
+ Note that even with `temperature` of `0.0`, the results will not be fully
+ deterministic.
"""
top_k: int
@@ -134,6 +155,9 @@ class MessageCreateParamsBase(TypedDict, total=False):
Used to remove "long tail" low probability responses.
[Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
+
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
"""
top_p: float
@@ -143,6 +167,9 @@ class MessageCreateParamsBase(TypedDict, total=False):
for each subsequent token in decreasing probability order and cut it off once it
reaches a particular probability specified by `top_p`. You should either alter
`temperature` or `top_p`, but not both.
+
+ Recommended for advanced use cases only. You usually only need to use
+ `temperature`.
"""
diff --git a/src/anthropic/types/message_delta_event.py b/src/anthropic/types/message_delta_event.py
index 1498b7f2..bc0530e5 100644
--- a/src/anthropic/types/message_delta_event.py
+++ b/src/anthropic/types/message_delta_event.py
@@ -21,4 +21,16 @@ class MessageDeltaEvent(BaseModel):
type: Literal["message_delta"]
usage: MessageDeltaUsage
- """Container for the number of tokens used."""
+ """Billing and rate-limit usage.
+
+ Anthropic's API bills and rate-limits by token counts, as tokens represent the
+ underlying cost to our systems.
+
+ Under the hood, the API transforms requests into a format suitable for the
+ model. The model's output then goes through a parsing stage before becoming an
+ API response. As a result, the token counts in `usage` will not match one-to-one
+ with the exact visible content of an API request or response.
+
+ For example, `output_tokens` will be non-zero, even for an empty string response
+ from Claude.
+ """
diff --git a/src/anthropic/types/message_param.py b/src/anthropic/types/message_param.py
index 7fc17d42..5b321957 100644
--- a/src/anthropic/types/message_param.py
+++ b/src/anthropic/types/message_param.py
@@ -7,11 +7,12 @@
from .content_block import ContentBlock
from .text_block_param import TextBlockParam
+from .image_block_param import ImageBlockParam
__all__ = ["MessageParam"]
class MessageParam(TypedDict, total=False):
- content: Required[Union[str, Iterable[Union[TextBlockParam, ContentBlock]]]]
+ content: Required[Union[str, Iterable[Union[TextBlockParam, ImageBlockParam, ContentBlock]]]]
role: Required[Literal["user", "assistant"]]
diff --git a/tests/api_resources/test_messages.py b/tests/api_resources/test_messages.py
index cc76a125..9198ffdd 100644
--- a/tests/api_resources/test_messages.py
+++ b/tests/api_resources/test_messages.py
@@ -24,10 +24,10 @@ def test_method_create_overload_1(self, client: Anthropic) -> None:
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
assert_matches_type(Message, message, path=["response"])
@@ -38,10 +38,10 @@ def test_method_create_with_all_params_overload_1(self, client: Anthropic) -> No
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"},
stop_sequences=["string", "string", "string"],
stream=False,
@@ -59,10 +59,10 @@ def test_raw_response_create_overload_1(self, client: Anthropic) -> None:
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
assert response.is_closed is True
@@ -77,10 +77,10 @@ def test_streaming_response_create_overload_1(self, client: Anthropic) -> None:
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -97,10 +97,10 @@ def test_method_create_overload_2(self, client: Anthropic) -> None:
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
)
message_stream.response.close()
@@ -112,10 +112,10 @@ def test_method_create_with_all_params_overload_2(self, client: Anthropic) -> No
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"},
stop_sequences=["string", "string", "string"],
@@ -133,10 +133,10 @@ def test_raw_response_create_overload_2(self, client: Anthropic) -> None:
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
)
@@ -151,10 +151,10 @@ def test_streaming_response_create_overload_2(self, client: Anthropic) -> None:
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
) as response:
assert not response.is_closed
@@ -176,10 +176,10 @@ async def test_method_create_overload_1(self, async_client: AsyncAnthropic) -> N
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
assert_matches_type(Message, message, path=["response"])
@@ -190,10 +190,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"},
stop_sequences=["string", "string", "string"],
stream=False,
@@ -211,10 +211,10 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncAnthropic
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
)
assert response.is_closed is True
@@ -229,10 +229,10 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncAnt
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -249,10 +249,10 @@ async def test_method_create_overload_2(self, async_client: AsyncAnthropic) -> N
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
)
await message_stream.response.aclose()
@@ -264,10 +264,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"},
stop_sequences=["string", "string", "string"],
@@ -285,10 +285,10 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncAnthropic
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
)
@@ -303,10 +303,10 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncAnt
messages=[
{
"role": "user",
- "content": "In one sentence, what is good about the color blue?",
+ "content": "Hello, world",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
stream=True,
) as response:
assert not response.is_closed
diff --git a/tests/lib/streaming/test_messages.py b/tests/lib/streaming/test_messages.py
index 1acbb26f..4d247238 100644
--- a/tests/lib/streaming/test_messages.py
+++ b/tests/lib/streaming/test_messages.py
@@ -24,7 +24,7 @@
# copied from the real API
stream_example = """
event: message_start
-data: {"type":"message_start","message":{"id":"msg_4QpJur2dWWDjF6C758FbBw5vm12BaVipnK","type":"message","role":"assistant","content":[],"model":"claude-2.1","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":11,"output_tokens":1}}}
+data: {"type":"message_start","message":{"id":"msg_4QpJur2dWWDjF6C758FbBw5vm12BaVipnK","type":"message","role":"assistant","content":[],"model":"claude-3-opus-20240229","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":11,"output_tokens":1}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
@@ -86,7 +86,7 @@ async def on_stream_event(self, event: MessageStreamEvent) -> None:
def assert_basic_response(stream: SyncEventTracker | AsyncEventTracker, message: Message) -> None:
assert message.id == "msg_4QpJur2dWWDjF6C758FbBw5vm12BaVipnK"
- assert message.model == "claude-2.1"
+ assert message.model == "claude-3-opus-20240229"
assert message.role == "assistant"
assert message.stop_reason == "end_turn"
assert message.stop_sequence is None
@@ -121,7 +121,7 @@ def test_basic_response(self, respx_mock: MockRouter) -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
event_handler=SyncEventTracker,
) as stream:
assert_basic_response(stream, stream.get_final_message())
@@ -138,7 +138,7 @@ def test_context_manager(self, respx_mock: MockRouter) -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as stream:
assert not stream.response.is_closed
@@ -160,7 +160,7 @@ async def test_basic_response(self, respx_mock: MockRouter) -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
event_handler=AsyncEventTracker,
) as stream:
assert_basic_response(stream, await stream.get_final_message())
@@ -178,7 +178,7 @@ async def test_context_manager(self, respx_mock: MockRouter) -> None:
"content": "Say hello there!",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
) as stream:
assert not stream.response.is_closed
diff --git a/tests/sample_file.txt b/tests/sample_file.txt
new file mode 100644
index 00000000..af5626b4
--- /dev/null
+++ b/tests/sample_file.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/tests/test_client.py b/tests/test_client.py
index 6d214499..7485975f 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -815,10 +815,10 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No
messages=[
{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
),
),
cast_to=httpx.Response,
@@ -842,10 +842,10 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non
messages=[
{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
),
),
cast_to=httpx.Response,
@@ -1633,10 +1633,10 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter)
messages=[
{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
),
),
cast_to=httpx.Response,
@@ -1660,10 +1660,10 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter)
messages=[
{
"role": "user",
- "content": "Where can I get a good coffee in my neighbourhood?",
+ "content": "Hello, Claude",
}
],
- model="claude-2.1",
+ model="claude-3-opus-20240229",
),
),
cast_to=httpx.Response,
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 2e1d9605..5e7687e9 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -1,22 +1,50 @@
from __future__ import annotations
-from typing import Any, List, Union, Iterable, Optional, cast
+import io
+import pathlib
+from typing import Any, List, Union, TypeVar, Iterable, Optional, cast
from datetime import date, datetime
from typing_extensions import Required, Annotated, TypedDict
import pytest
-from anthropic._utils import PropertyInfo, transform, parse_datetime
+from anthropic._types import Base64FileInput
+from anthropic._utils import (
+ PropertyInfo,
+ transform as _transform,
+ parse_datetime,
+ async_transform as _async_transform,
+)
from anthropic._compat import PYDANTIC_V2
from anthropic._models import BaseModel
+_T = TypeVar("_T")
+
+SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt")
+
+
+async def transform(
+ data: _T,
+ expected_type: object,
+ use_async: bool,
+) -> _T:
+ if use_async:
+ return await _async_transform(data, expected_type=expected_type)
+
+ return _transform(data, expected_type=expected_type)
+
+
+parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"])
+
class Foo1(TypedDict):
foo_bar: Annotated[str, PropertyInfo(alias="fooBar")]
-def test_top_level_alias() -> None:
- assert transform({"foo_bar": "hello"}, expected_type=Foo1) == {"fooBar": "hello"}
+@parametrize
+@pytest.mark.asyncio
+async def test_top_level_alias(use_async: bool) -> None:
+ assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"}
class Foo2(TypedDict):
@@ -32,9 +60,11 @@ class Baz2(TypedDict):
my_baz: Annotated[str, PropertyInfo(alias="myBaz")]
-def test_recursive_typeddict() -> None:
- assert transform({"bar": {"this_thing": 1}}, Foo2) == {"bar": {"this__thing": 1}}
- assert transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2) == {"bar": {"Baz": {"myBaz": "foo"}}}
+@parametrize
+@pytest.mark.asyncio
+async def test_recursive_typeddict(use_async: bool) -> None:
+ assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}}
+ assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}}
class Foo3(TypedDict):
@@ -45,8 +75,10 @@ class Bar3(TypedDict):
my_field: Annotated[str, PropertyInfo(alias="myField")]
-def test_list_of_typeddict() -> None:
- result = transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, expected_type=Foo3)
+@parametrize
+@pytest.mark.asyncio
+async def test_list_of_typeddict(use_async: bool) -> None:
+ result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async)
assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]}
@@ -62,10 +94,14 @@ class Baz4(TypedDict):
foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")]
-def test_union_of_typeddict() -> None:
- assert transform({"foo": {"foo_bar": "bar"}}, Foo4) == {"foo": {"fooBar": "bar"}}
- assert transform({"foo": {"foo_baz": "baz"}}, Foo4) == {"foo": {"fooBaz": "baz"}}
- assert transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4) == {"foo": {"fooBaz": "baz", "fooBar": "bar"}}
+@parametrize
+@pytest.mark.asyncio
+async def test_union_of_typeddict(use_async: bool) -> None:
+ assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}}
+ assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}}
+ assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == {
+ "foo": {"fooBaz": "baz", "fooBar": "bar"}
+ }
class Foo5(TypedDict):
@@ -80,9 +116,11 @@ class Baz5(TypedDict):
foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")]
-def test_union_of_list() -> None:
- assert transform({"foo": {"foo_bar": "bar"}}, Foo5) == {"FOO": {"fooBar": "bar"}}
- assert transform(
+@parametrize
+@pytest.mark.asyncio
+async def test_union_of_list(use_async: bool) -> None:
+ assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}}
+ assert await transform(
{
"foo": [
{"foo_baz": "baz"},
@@ -90,6 +128,7 @@ def test_union_of_list() -> None:
]
},
Foo5,
+ use_async,
) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]}
@@ -97,8 +136,10 @@ class Foo6(TypedDict):
bar: Annotated[str, PropertyInfo(alias="Bar")]
-def test_includes_unknown_keys() -> None:
- assert transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6) == {
+@parametrize
+@pytest.mark.asyncio
+async def test_includes_unknown_keys(use_async: bool) -> None:
+ assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == {
"Bar": "bar",
"baz_": {"FOO": 1},
}
@@ -113,9 +154,11 @@ class Bar7(TypedDict):
foo: str
-def test_ignores_invalid_input() -> None:
- assert transform({"bar": ""}, Foo7) == {"bAr": ""}
- assert transform({"foo": ""}, Foo7) == {"foo": ""}
+@parametrize
+@pytest.mark.asyncio
+async def test_ignores_invalid_input(use_async: bool) -> None:
+ assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""}
+ assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""}
class DatetimeDict(TypedDict, total=False):
@@ -134,52 +177,66 @@ class DateDict(TypedDict, total=False):
foo: Annotated[date, PropertyInfo(format="iso8601")]
-def test_iso8601_format() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_iso8601_format(use_async: bool) -> None:
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
- assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
+ assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
dt = dt.replace(tzinfo=None)
- assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap]
+ assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap]
- assert transform({"foo": None}, DateDict) == {"foo": None} # type: ignore[comparison-overlap]
- assert transform({"foo": date.fromisoformat("2023-02-23")}, DateDict) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap]
+ assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap]
+ assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap]
-def test_optional_iso8601_format() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_optional_iso8601_format(use_async: bool) -> None:
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
- assert transform({"bar": dt}, DatetimeDict) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
+ assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
- assert transform({"bar": None}, DatetimeDict) == {"bar": None}
+ assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None}
-def test_required_iso8601_format() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_required_iso8601_format(use_async: bool) -> None:
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
- assert transform({"required": dt}, DatetimeDict) == {"required": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
+ assert await transform({"required": dt}, DatetimeDict, use_async) == {
+ "required": "2023-02-23T14:16:36.337692+00:00"
+ } # type: ignore[comparison-overlap]
- assert transform({"required": None}, DatetimeDict) == {"required": None}
+ assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None}
-def test_union_datetime() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_union_datetime(use_async: bool) -> None:
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
- assert transform({"union": dt}, DatetimeDict) == { # type: ignore[comparison-overlap]
+ assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap]
"union": "2023-02-23T14:16:36.337692+00:00"
}
- assert transform({"union": "foo"}, DatetimeDict) == {"union": "foo"}
+ assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"}
-def test_nested_list_iso6801_format() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_nested_list_iso6801_format(use_async: bool) -> None:
dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
dt2 = parse_datetime("2022-01-15T06:34:23Z")
- assert transform({"list_": [dt1, dt2]}, DatetimeDict) == { # type: ignore[comparison-overlap]
+ assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap]
"list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"]
}
-def test_datetime_custom_format() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_datetime_custom_format(use_async: bool) -> None:
dt = parse_datetime("2022-01-15T06:34:23Z")
- result = transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")])
+ result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async)
assert result == "06" # type: ignore[comparison-overlap]
@@ -187,47 +244,59 @@ class DateDictWithRequiredAlias(TypedDict, total=False):
required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]]
-def test_datetime_with_alias() -> None:
- assert transform({"required_prop": None}, DateDictWithRequiredAlias) == {"prop": None} # type: ignore[comparison-overlap]
- assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == {
- "prop": "2023-02-23"
- } # type: ignore[comparison-overlap]
+@parametrize
+@pytest.mark.asyncio
+async def test_datetime_with_alias(use_async: bool) -> None:
+ assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap]
+ assert await transform(
+ {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async
+ ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap]
class MyModel(BaseModel):
foo: str
-def test_pydantic_model_to_dictionary() -> None:
- assert transform(MyModel(foo="hi!"), Any) == {"foo": "hi!"}
- assert transform(MyModel.construct(foo="hi!"), Any) == {"foo": "hi!"}
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_model_to_dictionary(use_async: bool) -> None:
+ assert await transform(MyModel(foo="hi!"), Any, use_async) == {"foo": "hi!"}
+ assert await transform(MyModel.construct(foo="hi!"), Any, use_async) == {"foo": "hi!"}
-def test_pydantic_empty_model() -> None:
- assert transform(MyModel.construct(), Any) == {}
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_empty_model(use_async: bool) -> None:
+ assert await transform(MyModel.construct(), Any, use_async) == {}
-def test_pydantic_unknown_field() -> None:
- assert transform(MyModel.construct(my_untyped_field=True), Any) == {"my_untyped_field": True}
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_unknown_field(use_async: bool) -> None:
+ assert await transform(MyModel.construct(my_untyped_field=True), Any, use_async) == {"my_untyped_field": True}
-def test_pydantic_mismatched_types() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_mismatched_types(use_async: bool) -> None:
model = MyModel.construct(foo=True)
if PYDANTIC_V2:
with pytest.warns(UserWarning):
- params = transform(model, Any)
+ params = await transform(model, Any, use_async)
else:
- params = transform(model, Any)
+ params = await transform(model, Any, use_async)
assert params == {"foo": True}
-def test_pydantic_mismatched_object_type() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_mismatched_object_type(use_async: bool) -> None:
model = MyModel.construct(foo=MyModel.construct(hello="world"))
if PYDANTIC_V2:
with pytest.warns(UserWarning):
- params = transform(model, Any)
+ params = await transform(model, Any, use_async)
else:
- params = transform(model, Any)
+ params = await transform(model, Any, use_async)
assert params == {"foo": {"hello": "world"}}
@@ -235,10 +304,12 @@ class ModelNestedObjects(BaseModel):
nested: MyModel
-def test_pydantic_nested_objects() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_nested_objects(use_async: bool) -> None:
model = ModelNestedObjects.construct(nested={"foo": "stainless"})
assert isinstance(model.nested, MyModel)
- assert transform(model, Any) == {"nested": {"foo": "stainless"}}
+ assert await transform(model, Any, use_async) == {"nested": {"foo": "stainless"}}
class ModelWithDefaultField(BaseModel):
@@ -247,24 +318,26 @@ class ModelWithDefaultField(BaseModel):
with_str_default: str = "foo"
-def test_pydantic_default_field() -> None:
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_default_field(use_async: bool) -> None:
# should be excluded when defaults are used
model = ModelWithDefaultField.construct()
assert model.with_none_default is None
assert model.with_str_default == "foo"
- assert transform(model, Any) == {}
+ assert await transform(model, Any, use_async) == {}
# should be included when the default value is explicitly given
model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo")
assert model.with_none_default is None
assert model.with_str_default == "foo"
- assert transform(model, Any) == {"with_none_default": None, "with_str_default": "foo"}
+ assert await transform(model, Any, use_async) == {"with_none_default": None, "with_str_default": "foo"}
# should be included when a non-default value is explicitly given
model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz")
assert model.with_none_default == "bar"
assert model.with_str_default == "baz"
- assert transform(model, Any) == {"with_none_default": "bar", "with_str_default": "baz"}
+ assert await transform(model, Any, use_async) == {"with_none_default": "bar", "with_str_default": "baz"}
class TypedDictIterableUnion(TypedDict):
@@ -279,21 +352,57 @@ class Baz8(TypedDict):
foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")]
-def test_iterable_of_dictionaries() -> None:
- assert transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "bar"}]}
- assert cast(Any, transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion)) == {"FOO": [{"fooBaz": "bar"}]}
+@parametrize
+@pytest.mark.asyncio
+async def test_iterable_of_dictionaries(use_async: bool) -> None:
+ assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == {
+ "FOO": [{"fooBaz": "bar"}]
+ }
+ assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == {
+ "FOO": [{"fooBaz": "bar"}]
+ }
def my_iter() -> Iterable[Baz8]:
yield {"foo_baz": "hello"}
yield {"foo_baz": "world"}
- assert transform({"foo": my_iter()}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}]}
+ assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == {
+ "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}]
+ }
class TypedDictIterableUnionStr(TypedDict):
foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")]
-def test_iterable_union_str() -> None:
- assert transform({"foo": "bar"}, TypedDictIterableUnionStr) == {"FOO": "bar"}
- assert cast(Any, transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]])) == [{"fooBaz": "bar"}]
+@parametrize
+@pytest.mark.asyncio
+async def test_iterable_union_str(use_async: bool) -> None:
+ assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"}
+ assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [
+ {"fooBaz": "bar"}
+ ]
+
+
+class TypedDictBase64Input(TypedDict):
+ foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_base64_file_input(use_async: bool) -> None:
+ # strings are left as-is
+ assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"}
+
+ # pathlib.Path is automatically converted to base64
+ assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == {
+ "foo": "SGVsbG8sIHdvcmxkIQo="
+ } # type: ignore[comparison-overlap]
+
+ # io instances are automatically converted to base64
+ assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == {
+ "foo": "SGVsbG8sIHdvcmxkIQ=="
+ } # type: ignore[comparison-overlap]
+ assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == {
+ "foo": "SGVsbG8sIHdvcmxkIQ=="
+ } # type: ignore[comparison-overlap]