Skip to content

Commit 240b490

Browse files
fix: bound anthropic<1.0.0 on the bedrock extra
The bedrock extra received the anthropic SDK only transitively via langchain-aws[anthropic] (unbounded above); langchain-anthropic 1.7.0 also lifted its own <1.0.0 pin. Fresh resolutions therefore picked anthropic 1.x, which moved to httpx2 and rejects UiPath's httpx clients at Bedrock chat-model construction. Add an explicit anthropic[bedrock]>=0.96.0,<1.0.0 to the bedrock extra (anthropic and all already carry the bound; no other extra pulls anthropic transitively), plus a guard test that fails if any extra's dependency closure includes anthropic without an explicit <1.0.0 bound. Interim pin — widen to <2.0.0 once httpx2-compatible clients land in uipath-llm-client. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 75f2c56 commit 240b490

5 files changed

Lines changed: 111 additions & 1 deletion

File tree

packages/uipath_langchain_client/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
All notable changes to `uipath_langchain_client` will be documented in this file.
44

5+
## [1.18.1] - 2026-08-31
6+
7+
### Fixed
8+
- Bound `anthropic[bedrock]>=0.96.0,<1.0.0` explicitly on the `bedrock` extra. The extra previously received the anthropic SDK only transitively (via `langchain-aws[anthropic]`, unbounded above; `langchain-anthropic>=1.7.0` also lifted its own `<1.0.0` pin), so fresh resolutions picked anthropic 1.x — which moved to httpx2 and rejects UiPath's httpx clients at Bedrock chat-model construction (`TypeError: Invalid http_client argument`). Interim pin: to be widened to `<2.0.0` once httpx2-compatible clients land in `uipath-llm-client`. Audited the remaining extras — no other extra pulls `anthropic` without the bound (`anthropic` and `all` already carry it) — and added a guard test enforcing the bound on any extra whose dependency closure includes `anthropic`.
9+
510
## [1.18.0] - 2026-08-13
611

712
### Changed

packages/uipath_langchain_client/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ anthropic = [
2222
]
2323
bedrock = [
2424
"langchain-aws[anthropic]>=1.4.5,<2.0.0",
25+
# Interim: langchain-aws[anthropic] and langchain-anthropic>=1.7.0 admit
26+
# anthropic 1.x, which requires httpx2 and rejects UiPath's httpx clients.
27+
# Widen to <2.0.0 once httpx2-compatible clients land in uipath-llm-client.
28+
"anthropic[bedrock]>=0.96.0,<1.0.0",
2529
]
2630
vertexai = [
2731
"langchain-google-vertexai>=3.2.2,<4.0.0",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__title__ = "UiPath LangChain Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services via LangChain."
3-
__version__ = "1.18.0"
3+
__version__ = "1.18.1"
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""Guard: every extra whose dependency closure includes `anthropic` must carry
2+
an explicit `anthropic` bound excluding 1.x.
3+
4+
The anthropic 1.x SDK moved to httpx2 and rejects UiPath's httpx-based clients
5+
at construction time, so until httpx2-compatible clients land the bound must be
6+
declared directly on each affected extra — transitive requirements
7+
(langchain-aws[anthropic], langchain-anthropic>=1.7.0) admit anthropic 1.x.
8+
9+
The closure is computed from the checked-in uv.lock (no network), and the bound
10+
is asserted against the extras declared in the langchain package's pyproject.
11+
When httpx2-compatible clients land and the pins are widened to <2.0.0, update
12+
EXCLUDED_ANTHROPIC_VERSION accordingly.
13+
"""
14+
15+
import tomllib
16+
from pathlib import Path
17+
from typing import Any
18+
19+
import pytest
20+
from packaging.requirements import Requirement
21+
22+
REPO_ROOT = Path(__file__).parents[2]
23+
LANGCHAIN_PYPROJECT = REPO_ROOT / "packages" / "uipath_langchain_client" / "pyproject.toml"
24+
UV_LOCK = REPO_ROOT / "uv.lock"
25+
26+
PACKAGE_NAME = "uipath-langchain-client"
27+
EXCLUDED_ANTHROPIC_VERSION = "1.0.0"
28+
29+
30+
def _load_extras() -> dict[str, list[Requirement]]:
31+
with LANGCHAIN_PYPROJECT.open("rb") as f:
32+
pyproject = tomllib.load(f)
33+
extras: dict[str, list[str]] = pyproject["project"]["optional-dependencies"]
34+
return {name: [Requirement(dep) for dep in deps] for name, deps in extras.items()}
35+
36+
37+
def _expand_self_references(extra: str, extras: dict[str, list[Requirement]]) -> list[Requirement]:
38+
"""Flatten extras composed from the package's own extras (e.g. `all`)."""
39+
requirements: list[Requirement] = []
40+
for requirement in extras[extra]:
41+
if requirement.name == PACKAGE_NAME:
42+
for referenced in requirement.extras:
43+
requirements.extend(_expand_self_references(referenced, extras))
44+
else:
45+
requirements.append(requirement)
46+
return requirements
47+
48+
49+
def _load_lock_graph() -> dict[str, dict[str, Any]]:
50+
with UV_LOCK.open("rb") as f:
51+
lock = tomllib.load(f)
52+
return {pkg["name"]: pkg for pkg in lock["package"]}
53+
54+
55+
def _closure(requirements: list[Requirement]) -> set[str]:
56+
"""Transitive dependency closure per uv.lock, honoring requirement extras."""
57+
graph = _load_lock_graph()
58+
reached: set[str] = set()
59+
visited: set[tuple[str, frozenset[str]]] = set()
60+
stack: list[tuple[str, frozenset[str]]] = [
61+
(req.name, frozenset(req.extras)) for req in requirements
62+
]
63+
while stack:
64+
name, extras = stack.pop()
65+
if (name, extras) in visited:
66+
continue
67+
visited.add((name, extras))
68+
reached.add(name)
69+
pkg = graph.get(name)
70+
if pkg is None:
71+
continue
72+
deps: list[dict[str, Any]] = list(pkg.get("dependencies", []))
73+
optional = pkg.get("optional-dependencies", {})
74+
for extra in extras:
75+
deps.extend(optional.get(extra, []))
76+
for dep in deps:
77+
stack.append((dep["name"], frozenset(dep.get("extra", []))))
78+
return reached
79+
80+
81+
@pytest.mark.parametrize("extra", sorted(_load_extras()))
82+
def test_extras_with_anthropic_in_closure_carry_explicit_bound(extra: str) -> None:
83+
extras = _load_extras()
84+
requirements = _expand_self_references(extra, extras)
85+
if "anthropic" not in _closure(requirements):
86+
return
87+
88+
anthropic_requirements = [req for req in requirements if req.name == "anthropic"]
89+
assert anthropic_requirements, (
90+
f"Extra '{extra}' pulls `anthropic` transitively but declares no explicit "
91+
f"`anthropic` requirement; add an `anthropic<{EXCLUDED_ANTHROPIC_VERSION}` "
92+
"bound to the extra in packages/uipath_langchain_client/pyproject.toml."
93+
)
94+
for requirement in anthropic_requirements:
95+
assert not requirement.specifier.contains(EXCLUDED_ANTHROPIC_VERSION, prereleases=True), (
96+
f"Extra '{extra}' admits anthropic=={EXCLUDED_ANTHROPIC_VERSION} via "
97+
f"'{requirement}'; the anthropic 1.x SDK (httpx2) is not yet supported."
98+
)

uv.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)