mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 10:17:55 +00:00
chore(deps): anthropic 1.5, openai 3.13, and the move to httpx2
Both SDKs' 1.x/3.x majors run on httpx2 (the maintained fork of httpx by its original author, published by Pydantic at github.com/pydantic/httpx2, version line 2.x) instead of httpx, which is what makes these two bumps one change. httpx2 is now a declared dependency because two modules import it directly. Everything else in the 0.x -> 1.x / 2.x -> 3.x change lists is absent here: no Text Completions, no `with_raw_response`, no raw `output_format` dicts, no Bedrock client, and `requires-python` is already 3.12. Three code changes, all forced by the bump: The BYOM DNS-pinning client in `docsgpt/security/safe_url.py` is handed to `OpenAI(http_client=...)`, and the SDK rejects an old-httpx client at construction — which would have taken the SSRF guard offline. It is built on httpx2 now, and its `sni_hostname` extension carries a `str` rather than ascii bytes: httpcore passes the value straight to `ssl.SSLContext.wrap_socket`, and the truststore backend httpx2 uses for the default system trust store encodes it instead of accepting bytes. Bytes therefore failed every real handshake while passing the existing tests, which stub the transport out; the test now pins the type and says why. The stream-retry error tuple in `docsgpt/llm/base.py` named `httpx` exceptions only. The two libraries' exception classes are unrelated types, so after the bump the retry silently stopped firing for openai and anthropic while still working for google-genai and elevenlabs. It now covers both stacks, with a parametrized test over each. anthropic 1.x dropped temperature/top_p/top_k from `messages.create`'s signature (passing one raises TypeError) without dropping them from the API, so the provider forwards them through `extra_body`. The wire request is unchanged and a model that rejects them 400s exactly as before.
This commit is contained in:
1 parent
b3326b3c33
commit
f7baa1952f
12 files changed
+237
-73
No files matched your search
@@ -537,7 +537,7 @@ def _capture_httpx_handle_request(monkeypatch):
|
||||
opening a real socket.
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import httpx2
|
||||
|
||||
captured: dict = {}
|
||||
|
||||
@@ -545,10 +545,10 @@ def _capture_httpx_handle_request(monkeypatch):
|
||||
captured["url"] = request.url
|
||||
captured["sni"] = request.extensions.get("sni_hostname")
|
||||
captured["host_header"] = request.headers.get("host")
|
||||
return httpx.Response(200, content=b"ok")
|
||||
return httpx2.Response(200, content=b"ok")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"httpx.HTTPTransport.handle_request", fake_handle
|
||||
"httpx2.HTTPTransport.handle_request", fake_handle
|
||||
)
|
||||
return captured
|
||||
|
||||
@@ -610,7 +610,14 @@ def test_pinned_httpx_transport_rewrites_url_to_validated_ip(monkeypatch):
|
||||
def test_pinned_httpx_transport_sets_sni_for_original_hostname(monkeypatch):
|
||||
"""TLS SNI / cert verification must use the original hostname; the
|
||||
transport sets it via the ``sni_hostname`` extension that
|
||||
httpcore forwards to ``start_tls``'s ``server_hostname``."""
|
||||
httpcore forwards to ``start_tls``'s ``server_hostname``.
|
||||
|
||||
The value must be a ``str``, not ascii bytes: httpcore passes it
|
||||
straight to ``ssl.SSLContext.wrap_socket``, and the ``truststore``
|
||||
backend httpx2 uses for the default system trust store encodes it
|
||||
rather than accepting bytes — so bytes fail every real handshake
|
||||
while passing any test that stubs the transport out.
|
||||
"""
|
||||
|
||||
captured = _capture_httpx_handle_request(monkeypatch)
|
||||
|
||||
@@ -623,7 +630,8 @@ def test_pinned_httpx_transport_sets_sni_for_original_hostname(monkeypatch):
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
assert captured["sni"] == b"api.example.com"
|
||||
assert captured["sni"] == "api.example.com"
|
||||
assert isinstance(captured["sni"], str)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -712,7 +720,7 @@ def test_pinned_httpx_transport_refuses_unexpected_host(monkeypatch):
|
||||
transport refuses rather than silently dialing the validated IP
|
||||
with a different host's credentials."""
|
||||
|
||||
import httpx
|
||||
import httpx2
|
||||
|
||||
with mock.patch(
|
||||
"socket.getaddrinfo", return_value=_addrinfo("104.18.6.192")
|
||||
@@ -721,7 +729,7 @@ def test_pinned_httpx_transport_refuses_unexpected_host(monkeypatch):
|
||||
try:
|
||||
with pytest.raises(UnsafeUserUrlError, match="refused request"):
|
||||
client.get("https://other.example.com/v1/test")
|
||||
except httpx.RequestError:
|
||||
except httpx2.RequestError:
|
||||
# Some httpx versions may wrap the transport error; accept
|
||||
# either path so long as the request didn't succeed.
|
||||
pass
|
||||
|
||||
Reference in new issue
Block a user