Skip to content

Commit 5ec92a6

Browse files
renovate[bot]kevinmessiaenhenchaves
authored
fix(deps): update dependency anthropic to v1 (#2810)
--------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kevin Messiaen <kevinmessiaen@users.noreply.github.com> Co-authored-by: Henrique Chaves <henrique@giskard.ai>
1 parent d16dda4 commit 5ec92a6

9 files changed

Lines changed: 239 additions & 20 deletions

File tree

THIRD_PARTY_NOTICES.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Find a list of packages below
2929
||cffi|
3030
||charset-normalizer|
3131
||click|
32+
||cloudpickle|
3233
||colorama|
3334
||cryptography|
3435
||defusedxml|
@@ -46,7 +47,9 @@ Find a list of packages below
4647
||h11|
4748
||hf-xet|
4849
||httpcore|
50+
||httpcore2|
4951
||httpx|
52+
||httpx2|
5053
||huggingface-hub|
5154
||idna|
5255
||importlib-metadata|
@@ -94,6 +97,7 @@ Find a list of packages below
9497
||tiktoken|
9598
||tokenizers|
9699
||tqdm|
100+
||truststore|
97101
||typing-extensions|
98102
||typing-inspection|
99103
||urllib3|
@@ -129,7 +133,7 @@ Find a list of packages below
129133
- License: MIT License
130134
- Compatible: True
131135

132-
### anthropic-0.111.0
136+
### anthropic-1.2.0
133137

134138
- HomePage:
135139
- Author: Anthropic
@@ -206,6 +210,13 @@ Find a list of packages below
206210
- License: BSD-3-Clause
207211
- Compatible: True
208212

213+
### cloudpickle-3.1.2
214+
215+
- HomePage: https://github.com/cloudpipe/cloudpickle
216+
- Author: The cloudpickle developer team
217+
- License: BSD License
218+
- Compatible: True
219+
209220
### colorama-0.4.6
210221

211222
- HomePage:
@@ -325,13 +336,27 @@ Find a list of packages below
325336
- License: BSD-3-Clause
326337
- Compatible: True
327338

339+
### httpcore2-2.12.0
340+
341+
- HomePage:
342+
- Author: Tom Christie
343+
- License: BSD-3-Clause
344+
- Compatible: True
345+
328346
### httpx-0.28.1
329347

330348
- HomePage:
331349
- Author: Tom Christie
332350
- License: BSD License
333351
- Compatible: True
334352

353+
### httpx2-2.12.0
354+
355+
- HomePage:
356+
- Author: Tom Christie
357+
- License: BSD-3-Clause
358+
- Compatible: True
359+
335360
### huggingface-hub-1.16.1
336361

337362
- HomePage: https://github.com/huggingface/huggingface_hub
@@ -661,6 +686,13 @@ Find a list of packages below
661686
- License: MIT;; MPL-2.0
662687
- Compatible: True
663688

689+
### truststore-0.10.4
690+
691+
- HomePage:
692+
- Author: Seth Michael Larson
693+
- License: MIT
694+
- Compatible: True
695+
664696
### typing-extensions-4.15.0
665697

666698
- HomePage:

libs/giskard-llm/README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ response = await client.acompletion(
6565
|---|---|---|---|---|---|
6666
| `openai/` (default) | `openai` | `OPENAI_API_KEY` | yes | yes | `base_url`, `timeout`, `http_client`, `default_headers` |
6767
| `google/` | `google-genai` | `GOOGLE_API_KEY` / `GEMINI_API_KEY` | yes | yes | `http_client`, `default_headers`, `http_options` |
68-
| `anthropic/` | `anthropic` | `ANTHROPIC_API_KEY` | yes | no | `merge_system`, `timeout`, `http_client`, `default_headers` |
68+
| `anthropic/` | `anthropic` | `ANTHROPIC_API_KEY` | yes | no | `merge_system`, `timeout`, `http_client` (`httpx2`, see below), `default_headers` |
6969
| `azure/` | `openai` | `AZURE_API_KEY`, `AZURE_API_BASE` | yes | yes | `api_version`, `base_url`, `http_client`, `default_headers` |
7070
| `azure_ai/` | `openai` | `AZURE_AI_API_KEY`, `AZURE_AI_ENDPOINT` | yes | model-dependent | `base_url`, `http_client`, `default_headers` |
7171

@@ -135,4 +135,19 @@ response = await client.acompletion("azure-secure/gpt-4.1-nano", messages)
135135
await http_client.aclose()
136136
```
137137

138+
The `anthropic` provider is the exception. Anthropic SDK v1 requires an
139+
`httpx2.AsyncClient` and raises a `TypeError` for an `httpx.AsyncClient`.
140+
141+
```python
142+
import httpx2
143+
144+
anthropic_http_client = httpx2.AsyncClient(verify="/path/to/ca.pem")
145+
client.configure(
146+
"anthropic-secure",
147+
provider="anthropic",
148+
api_key="os.environ/ANTHROPIC_API_KEY", # pragma: allowlist secret
149+
http_client=anthropic_http_client,
150+
)
151+
```
152+
138153
For detailed per-provider documentation (role mapping, message constraints, tool format, error mapping), see the provider class docstrings in `src/giskard/llm/providers/`.

libs/giskard-llm/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ dependencies = [
1414
[project.optional-dependencies]
1515
openai = ["openai>=1.30,<4"]
1616
google = ["google-genai>=2.0,<3"]
17-
anthropic = ["anthropic>=0.40,<1"]
17+
anthropic = ["anthropic>=1,<2"]
1818
azure = ["giskard-llm[openai]"] # alias: azure/ and azure_ai/ use the openai SDK
1919
all = ["giskard-llm[openai,google,anthropic]"]
2020

libs/giskard-llm/src/giskard/llm/providers/anthropic.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
- ``merge_system``: if True, concatenate multiple system messages instead of raising
4242
- ``base_url``: custom API endpoint
4343
- ``timeout``: request timeout in seconds
44-
- ``http_client``: caller-owned async HTTP client passed to the SDK; not closed by giskard-llm
44+
- ``http_client``: caller-owned ``httpx2.AsyncClient`` passed to the SDK; not closed by giskard-llm.
45+
Anthropic SDK v1 requires httpx2 (passing ``httpx.AsyncClient`` raises TypeError).
4546
- ``default_headers``: extra headers merged into every SDK request
4647
"""
4748

@@ -73,7 +74,7 @@
7374
from ..utils import compact
7475

7576
if TYPE_CHECKING:
76-
from httpx import AsyncClient
77+
from httpx2 import AsyncClient
7778

7879
logger = logging.getLogger(__name__)
7980

libs/giskard-llm/src/giskard/llm/translators/anthropic.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
from collections.abc import Iterable, Sequence
33
from typing import TYPE_CHECKING, Any, Literal, Required, TypedDict, cast
44

5-
from pydantic import BaseModel, SerializationInfo, field_serializer, model_validator
5+
from pydantic import (
6+
BaseModel,
7+
SerializationInfo,
8+
field_serializer,
9+
field_validator,
10+
model_validator,
11+
)
612

713
from ..types import (
814
AssistantMessage,
@@ -31,15 +37,16 @@
3137
from anthropic.types.text_block_param import TextBlockParam
3238
from anthropic.types.tool_union_param import ToolUnionParam
3339
from anthropic.types.tool_use_block_param import ToolUseBlockParam
34-
from httpx import Timeout as httpxTimeout
40+
from httpx2 import Timeout as httpxTimeout
3541

3642
class CompletionCreateParams(TypedDict, total=False):
3743
messages: Required[Sequence[MessageParam]]
3844
model: Required[ModelParam]
3945
max_tokens: Required[int]
4046
tools: Sequence[ToolUnionParam]
4147
system: str | list[TextBlockParam]
42-
temperature: float
48+
extra_body: dict[str, object]
49+
stop_sequences: Sequence[str]
4350
timeout: float | httpxTimeout | None
4451
output_config: OutputConfigParam
4552
else:
@@ -52,6 +59,7 @@ class CompletionCreateParams(TypedDict, total=False):
5259
KNOWN_COMPLETION_PARAMS = frozenset(
5360
{
5461
"max_tokens",
62+
"stop_sequences",
5563
"temperature",
5664
"timeout",
5765
"tools",
@@ -184,9 +192,25 @@ class AnthropicChatConfigParams(_BaseModel):
184192
tools: Sequence[ToolDef] | None = None
185193
system: str | list[SystemTextBlock] | None = None
186194
temperature: float | None = None
195+
stop_sequences: Sequence[str] | None = None
187196
timeout: float | httpxTimeout | None = None
188197
output_config: dict[str, object] | None = None
189198

199+
@field_validator("timeout", mode="before")
200+
@classmethod
201+
def _coerce_timeout(cls, v: Any) -> Any:
202+
# httpx2 mis-parses an httpx (v1) Timeout as a scalar; convert it.
203+
# isinstance, not __module__: SDKs relabel re-exported httpx classes.
204+
try:
205+
from httpx import Timeout as HttpxV1Timeout
206+
except ImportError:
207+
return v
208+
if isinstance(v, HttpxV1Timeout):
209+
from httpx2 import Timeout
210+
211+
return Timeout(connect=v.connect, read=v.read, write=v.write, pool=v.pool)
212+
return v
213+
190214
@field_serializer("messages")
191215
def serialize_messages(
192216
self, values: Sequence[ChatMessage], info: SerializationInfo
@@ -279,13 +303,19 @@ def to_anthropic(
279303
**params,
280304
)
281305

282-
return cast(
283-
"CompletionCreateParams",
306+
payload = cast(
307+
dict[str, Any],
284308
cast(
285309
object,
286310
anthropic_params.model_dump(context={"provider": _PROVIDER}),
287311
),
288312
)
313+
# SDK v1 dropped sampling kwargs on messages.create (TypeError). Keep the
314+
# public temperature API by forwarding it through extra_body.
315+
temperature = payload.pop("temperature", None)
316+
if temperature is not None:
317+
payload["extra_body"] = {"temperature": temperature}
318+
return cast("CompletionCreateParams", cast(object, payload))
289319

290320
@staticmethod
291321
def block_content_to_giskard(

libs/giskard-llm/tests/functional/test_completion.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,19 @@ async def test_configure_explicit(provider: str):
412412
assert resp.choices[0].message.text
413413

414414

415+
# -- Provider-specific parameter scenarios ------------------------------------
416+
417+
418+
@pytest.mark.anthropic
419+
async def test_anthropic_temperature_accepted():
420+
"""SDK v1 forwards temperature via extra_body; the live API must accept it."""
421+
client, model = _make_client("anthropic")
422+
resp = await client.acompletion(
423+
model, [{"role": "user", "content": "Say hello"}], temperature=0.2
424+
)
425+
assert resp.choices[0].message.text
426+
427+
415428
# -- Error handling scenarios --------------------------------------------------
416429

417430

libs/giskard-llm/tests/test_providers.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
if TYPE_CHECKING:
3434
from google.genai.types import HttpOptionsOrDict
3535
from httpx import AsyncClient
36+
from httpx2 import AsyncClient as Httpx2AsyncClient
3637

3738
# -- Helpers -------------------------------------------------------------------
3839

@@ -350,7 +351,7 @@ def test_anthropic_provider_forwards_transport_config():
350351
api_key="k",
351352
base_url="https://anthropic.test",
352353
timeout=12,
353-
http_client=cast("AsyncClient", http_client),
354+
http_client=cast("Httpx2AsyncClient", http_client),
354355
default_headers=default_headers,
355356
)
356357

@@ -362,6 +363,36 @@ def test_anthropic_provider_forwards_transport_config():
362363
assert kwargs["default_headers"] == default_headers
363364

364365

366+
async def test_sdk_v1_async_anthropic_accepts_httpx2_http_client():
367+
"""Unmocked SDK check: v1 requires httpx2.AsyncClient for http_client."""
368+
pytest.importorskip("anthropic")
369+
pytest.importorskip("httpx2")
370+
import httpx2
371+
from anthropic import AsyncAnthropic
372+
373+
http_client = httpx2.AsyncClient()
374+
try:
375+
client = AsyncAnthropic(api_key="sk-test", http_client=http_client)
376+
assert client is not None
377+
finally:
378+
# Caller-owned: giskard-llm does not close this; the test must.
379+
await http_client.aclose()
380+
381+
382+
async def test_sdk_v1_async_anthropic_rejects_httpx_v1_http_client():
383+
"""Unmocked SDK check: v1 raises TypeError for an httpx (v1) AsyncClient."""
384+
pytest.importorskip("anthropic")
385+
httpx = pytest.importorskip("httpx")
386+
from anthropic import AsyncAnthropic
387+
388+
http_client = httpx.AsyncClient()
389+
try:
390+
with pytest.raises(TypeError, match="http_client"):
391+
AsyncAnthropic(api_key="sk-test", http_client=http_client)
392+
finally:
393+
await http_client.aclose()
394+
395+
365396
class _FakeHttpOptions:
366397
def __init__(self, httpxAsyncClient=None, headers=None):
367398
self.httpx_async_client = httpxAsyncClient

libs/giskard-llm/tests/translators/test_anthropic_chat.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,59 @@ def test_unknown_completion_params_warn(caplog):
411411
payload = AnthropicChatTranslator.to_anthropic(
412412
_MODEL, [msg], top_p=0.5, stop_sequences=["STOP"], temperature=0.2
413413
)
414-
assert payload.get("temperature") == 0.2
414+
assert "temperature" not in payload
415+
assert payload.get("extra_body") == {"temperature": 0.2}
415416
assert "top_p" not in payload
416-
assert "stop_sequences" not in payload
417+
assert payload.get("stop_sequences") == ["STOP"]
417418
joined = " ".join(r.message for r in caplog.records)
418419
assert "top_p" in joined
419-
assert "stop_sequences" in joined
420+
assert "stop_sequences" not in joined
421+
422+
423+
def test_sdk_v1_messages_create_accepts_translator_payload():
424+
"""Unmocked SDK check: v1 rejects top-level temperature but accepts extra_body."""
425+
pytest.importorskip("anthropic")
426+
import inspect
427+
428+
from anthropic.resources.messages.messages import AsyncMessages
429+
430+
payload = AnthropicChatTranslator.to_anthropic(
431+
_MODEL,
432+
[UserMessage(content="Hello.")],
433+
temperature=0.2,
434+
stop_sequences=["STOP"],
435+
)
436+
assert "temperature" not in payload
437+
assert payload.get("extra_body") == {"temperature": 0.2}
438+
assert payload.get("stop_sequences") == ["STOP"]
439+
inspect.signature(AsyncMessages.create).bind_partial(**payload)
440+
441+
442+
def test_httpx_v1_timeout_converted_to_httpx2():
443+
"""An httpx (v1) Timeout is converted, not mis-parsed as a scalar by httpx2."""
444+
httpx = pytest.importorskip("httpx")
445+
pytest.importorskip("httpx2")
446+
from httpx2 import Timeout
447+
448+
payload = AnthropicChatTranslator.to_anthropic(
449+
_MODEL,
450+
[UserMessage(content="Hello.")],
451+
timeout=httpx.Timeout(5.0, connect=2.0),
452+
)
453+
timeout = payload.get("timeout")
454+
assert isinstance(timeout, Timeout)
455+
assert timeout.connect == 2.0
456+
assert timeout.read == 5.0
457+
assert timeout.write == 5.0
458+
assert timeout.pool == 5.0
459+
460+
461+
def test_httpx2_timeout_passes_through():
462+
"""A native httpx2 Timeout is forwarded untouched."""
463+
httpx2 = pytest.importorskip("httpx2")
464+
465+
original = httpx2.Timeout(5.0, connect=2.0)
466+
payload = AnthropicChatTranslator.to_anthropic(
467+
_MODEL, [UserMessage(content="Hello.")], timeout=original
468+
)
469+
assert payload.get("timeout") is original

0 commit comments

Comments
 (0)