mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
chore(deps): bump httpx to 0.28 and litellm to 1.84, shimming cashu's removed proxies kwarg
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ from openai import OpenAI
|
||||
client = OpenAI(
|
||||
api_key=os.environ.get("TOKEN"),
|
||||
base_url=os.environ.get("ONION_URL", "http://roustrjfsdgfiueghsklchg.onion/v1"),
|
||||
http_client=httpx.Client(proxies="socks5://localhost:9050"),
|
||||
http_client=httpx.Client(proxy="socks5://localhost:9050"),
|
||||
)
|
||||
|
||||
print(
|
||||
|
||||
+9
-5
@@ -9,7 +9,7 @@ dependencies = [
|
||||
"fastapi[standard-no-fastapi-cloud-cli]>=0.141",
|
||||
"aiosqlite>=0.20",
|
||||
"sqlmodel>=0.0.24",
|
||||
"httpx[socks]>=0.25.2",
|
||||
"httpx[socks]>=0.28.1",
|
||||
"h11>=0.16",
|
||||
"greenlet>=3.2.1",
|
||||
"alembic>=1.13",
|
||||
@@ -21,7 +21,7 @@ dependencies = [
|
||||
"mdurl==0.1.2",
|
||||
"pillow>=10",
|
||||
"openai>=1.98.0",
|
||||
"litellm>=1.55.0",
|
||||
"litellm>=1.84.0,<1.85",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
@@ -88,11 +88,15 @@ disallow_untyped_decorators = true
|
||||
[tool.uv.sources]
|
||||
routstr = { workspace = true }
|
||||
|
||||
# Security floors. cashu 0.20.x caps h11, fastapi, cryptography, setuptools
|
||||
# and wheel below their patched versions. routstr uses only cashu's wallet
|
||||
# modules, not its server paths, so the caps are safe to lift here.
|
||||
# Security floors. cashu 0.20.x caps httpx, h11, fastapi, cryptography,
|
||||
# setuptools and wheel below their patched versions. routstr uses only cashu's
|
||||
# wallet modules, not its server paths, so the caps are safe to lift here.
|
||||
[tool.uv]
|
||||
override-dependencies = [
|
||||
# httpx 0.28 (required by litellm 1.84) removed the `proxies` kwarg cashu
|
||||
# passes on every mint call; routstr/cashu_compat.py restores it for cashu.
|
||||
"httpx[socks]>=0.28.1,<1.0",
|
||||
"importlib-metadata>=8.0.0,<9.0",
|
||||
"h11>=0.16.0",
|
||||
"fastapi[standard-no-fastapi-cloud-cli]>=0.141",
|
||||
"cryptography>=49.0.0",
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
"""Compatibility shim that keeps cashu 0.20.x working on httpx>=0.28.
|
||||
|
||||
cashu's ``async_set_httpx_client`` decorator builds the client for *every* mint
|
||||
call as ``httpx.AsyncClient(proxies=proxies_dict, ...)``. httpx deprecated
|
||||
``proxies`` in 0.26 and removed it in 0.28, so on httpx>=0.28 every wallet
|
||||
operation routstr performs -- ``load_mint_keysets``, ``mint_quote``,
|
||||
``melt_quote``, token redeem -- raises::
|
||||
|
||||
TypeError: AsyncClient.__init__() got an unexpected keyword argument 'proxies'
|
||||
|
||||
Upstream cashu (0.20.3, the latest release) still caps ``httpx<0.26`` and has no
|
||||
release that fixes this, while litellm>=1.84 requires ``httpx>=0.28``. We bridge
|
||||
the gap by translating the keyword *inside cashu's module namespace only* --
|
||||
global httpx behaviour is untouched, and the shim is a no-op on httpx<0.28.
|
||||
|
||||
Delete this module once cashu ships a release that passes ``proxy=``.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
__all__ = ["install_cashu_httpx_shim"]
|
||||
|
||||
_ALL_SCHEMES = "all://"
|
||||
|
||||
|
||||
def _single_proxy(proxies: Any) -> str | None:
|
||||
"""Collapse an httpx<0.28 ``proxies`` mapping into a single ``proxy`` URL.
|
||||
|
||||
cashu only ever builds ``{}`` or ``{"all://": url}``, so a mapping with one
|
||||
distinct URL is all we need to support; anything richer is unrepresentable
|
||||
as httpx 0.28's scalar ``proxy=`` and is dropped rather than guessed at.
|
||||
"""
|
||||
if not proxies:
|
||||
return None
|
||||
if isinstance(proxies, str):
|
||||
return proxies
|
||||
if isinstance(proxies, dict):
|
||||
if _ALL_SCHEMES in proxies:
|
||||
value = proxies[_ALL_SCHEMES]
|
||||
return str(value) if value is not None else None
|
||||
distinct = {str(v) for v in proxies.values() if v is not None}
|
||||
if len(distinct) == 1:
|
||||
return distinct.pop()
|
||||
return None
|
||||
|
||||
|
||||
class _ProxiesCompatAsyncClient(httpx.AsyncClient):
|
||||
"""``httpx.AsyncClient`` that still accepts the removed ``proxies`` kwarg."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
if "proxies" in kwargs:
|
||||
proxies = kwargs.pop("proxies")
|
||||
proxy = _single_proxy(proxies)
|
||||
if proxy is not None and kwargs.get("proxy") is None:
|
||||
kwargs["proxy"] = proxy
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class _HttpxNamespace:
|
||||
"""Stand-in for the ``httpx`` module inside cashu's ``v1_api``.
|
||||
|
||||
Every attribute resolves against the real module except ``AsyncClient``,
|
||||
so cashu keeps using genuine httpx types everywhere else.
|
||||
"""
|
||||
|
||||
AsyncClient = _ProxiesCompatAsyncClient
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(httpx, name)
|
||||
|
||||
|
||||
def _httpx_accepts_proxies() -> bool:
|
||||
import inspect
|
||||
|
||||
try:
|
||||
return "proxies" in inspect.signature(httpx.AsyncClient.__init__).parameters
|
||||
except (TypeError, ValueError): # pragma: no cover - defensive
|
||||
return False
|
||||
|
||||
|
||||
def install_cashu_httpx_shim() -> bool:
|
||||
"""Patch cashu's mint client to survive httpx>=0.28.
|
||||
|
||||
Returns True when the shim was installed, False when it wasn't needed.
|
||||
Safe to call repeatedly.
|
||||
"""
|
||||
if _httpx_accepts_proxies():
|
||||
return False
|
||||
|
||||
from cashu.wallet import v1_api
|
||||
|
||||
if isinstance(getattr(v1_api, "httpx", None), _HttpxNamespace):
|
||||
return True
|
||||
|
||||
v1_api.httpx = _HttpxNamespace() # type: ignore[assignment]
|
||||
return True
|
||||
@@ -320,18 +320,18 @@ async def fetch_provider_health(endpoint_url: str) -> dict[str, Any]:
|
||||
is_onion = ".onion" in endpoint_url
|
||||
|
||||
# Set up client arguments conditionally
|
||||
proxies = None
|
||||
proxy: str | None = None
|
||||
if is_onion:
|
||||
try:
|
||||
tor_proxy = settings.tor_proxy_url
|
||||
except Exception:
|
||||
tor_proxy = "socks5://127.0.0.1:9050"
|
||||
proxies = {"http://": tor_proxy, "https://": tor_proxy} # type: ignore[assignment]
|
||||
proxy = tor_proxy
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
timeout=httpx.Timeout(30.0),
|
||||
follow_redirects=True,
|
||||
proxies=proxies, # type: ignore[arg-type]
|
||||
proxy=proxy,
|
||||
) as client:
|
||||
# Prefer provider's /v1/info for full details
|
||||
info_url = f"{endpoint_url.rstrip('/')}/v1/info"
|
||||
|
||||
@@ -8,12 +8,17 @@ import httpx
|
||||
from cashu.core.base import MeltQuoteState
|
||||
from cashu.wallet.wallet import Proof, Wallet
|
||||
|
||||
from ..cashu_compat import install_cashu_httpx_shim
|
||||
from ..mint import (
|
||||
is_mint_rate_limited,
|
||||
is_mint_transport_error,
|
||||
run_mint_operation,
|
||||
)
|
||||
|
||||
# cashu 0.20.x passes the `proxies` kwarg httpx removed in 0.28; see the module
|
||||
# docstring. Installed at import so no mint call can run before the patch.
|
||||
install_cashu_httpx_shim()
|
||||
|
||||
try:
|
||||
from bech32 import bech32_decode, convertbits # type: ignore
|
||||
except ModuleNotFoundError: # pragma: no cover – allow runtime miss
|
||||
|
||||
@@ -20,6 +20,7 @@ from cashu.wallet.wallet import Wallet as _CashuWallet
|
||||
from pydantic_core import PydanticUndefined
|
||||
from sqlmodel import col, select, update
|
||||
|
||||
from .cashu_compat import install_cashu_httpx_shim
|
||||
from .core import db, get_logger
|
||||
from .core.db import store_cashu_transaction_with_retry as store_cashu_transaction
|
||||
from .core.settings import settings
|
||||
@@ -36,6 +37,10 @@ from .mint import (
|
||||
)
|
||||
from .payment.lnurl import raw_send_to_lnurl
|
||||
|
||||
# cashu 0.20.x passes the `proxies` kwarg httpx removed in 0.28; see the module
|
||||
# docstring. Installed at import so no mint call can run before the patch.
|
||||
install_cashu_httpx_shim()
|
||||
|
||||
# Backwards-compatible aliases for callers/tests that imported the former
|
||||
# wallet-local policy. Production modules use the public routstr.mint API.
|
||||
_MintRateGuard = MintRateGuard
|
||||
|
||||
@@ -410,12 +410,19 @@ async def test_malformed_auxiliary_rate_is_rejected(
|
||||
("input_cache_write", float("-inf")),
|
||||
("completion", -1.0),
|
||||
):
|
||||
# Send raw bytes rather than `json=`: httpx>=0.28 refuses to encode
|
||||
# non-finite floats itself (allow_nan=False), but the point of this
|
||||
# test is that the SERVER answers the bare NaN/Infinity literals
|
||||
# with a 422, so the literals must still reach it.
|
||||
body = json.dumps(
|
||||
_payload(
|
||||
provider_id, model_id="aux-rate", pricing=_pricing(**{field: bad})
|
||||
)
|
||||
).encode("utf-8")
|
||||
resp = await integration_client.post(
|
||||
f"/admin/api/upstream-providers/{provider_id}/models",
|
||||
headers=_admin_headers(),
|
||||
json=_payload(
|
||||
provider_id, model_id="aux-rate", pricing=_pricing(**{field: bad})
|
||||
),
|
||||
headers={**_admin_headers(), "content-type": "application/json"},
|
||||
content=body,
|
||||
)
|
||||
|
||||
assert resp.status_code == 422, field
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"""cashu 0.20.x builds its mint client with the `proxies` kwarg httpx removed in
|
||||
0.28. These tests pin the shim that keeps every wallet call working."""
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from cashu.wallet import v1_api
|
||||
from httpx import AsyncClient
|
||||
|
||||
from routstr.cashu_compat import (
|
||||
_ProxiesCompatAsyncClient,
|
||||
_single_proxy,
|
||||
install_cashu_httpx_shim,
|
||||
)
|
||||
|
||||
|
||||
def test_httpx_no_longer_accepts_proxies() -> None:
|
||||
"""The premise of the shim: plain httpx rejects what cashu passes."""
|
||||
with pytest.raises(TypeError):
|
||||
httpx.AsyncClient(proxies={}) # type: ignore[call-arg]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"proxies, expected",
|
||||
[
|
||||
({}, None),
|
||||
(None, None),
|
||||
({"all://": "socks5://localhost:9050"}, "socks5://localhost:9050"),
|
||||
("socks5://localhost:9050", "socks5://localhost:9050"),
|
||||
({"http://": "http://p:1", "https://": "http://p:1"}, "http://p:1"),
|
||||
({"http://": "http://a:1", "https://": "http://b:2"}, None),
|
||||
],
|
||||
)
|
||||
def test_single_proxy_collapses_cashu_mappings(
|
||||
proxies: object, expected: str | None
|
||||
) -> None:
|
||||
assert _single_proxy(proxies) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("proxies", [{}, {"all://": "socks5://localhost:9050"}])
|
||||
async def test_compat_client_accepts_proxies(proxies: dict) -> None:
|
||||
async with _ProxiesCompatAsyncClient(
|
||||
proxies=proxies, base_url="http://mint.test"
|
||||
) as client:
|
||||
assert isinstance(client, httpx.AsyncClient)
|
||||
|
||||
|
||||
async def test_cashu_decorator_builds_a_client_after_shim() -> None:
|
||||
"""The real cashu decorator — the code path every mint call goes through."""
|
||||
install_cashu_httpx_shim()
|
||||
|
||||
class _Ledger:
|
||||
url = "http://mint.test/"
|
||||
# cashu's decorator assigns the client here; alias avoids shadowing.
|
||||
httpx: AsyncClient
|
||||
|
||||
@v1_api.async_set_httpx_client # type: ignore[misc]
|
||||
async def call(self) -> AsyncClient:
|
||||
return self.httpx
|
||||
|
||||
client: httpx.AsyncClient = await _Ledger().call()
|
||||
try:
|
||||
assert isinstance(client, httpx.AsyncClient)
|
||||
assert str(client.base_url) == "http://mint.test"
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
|
||||
def test_install_is_idempotent() -> None:
|
||||
assert install_cashu_httpx_shim() is True
|
||||
patched = v1_api.httpx
|
||||
assert install_cashu_httpx_shim() is True
|
||||
assert v1_api.httpx is patched
|
||||
|
||||
|
||||
def test_shim_namespace_passes_through_other_httpx_attributes() -> None:
|
||||
install_cashu_httpx_shim()
|
||||
assert v1_api.httpx.Response is httpx.Response
|
||||
assert v1_api.httpx.AsyncClient is _ProxiesCompatAsyncClient
|
||||
@@ -16,6 +16,8 @@ overrides = [
|
||||
{ name = "cryptography", specifier = ">=49.0.0" },
|
||||
{ name = "fastapi", extras = ["standard-no-fastapi-cloud-cli"], specifier = ">=0.141" },
|
||||
{ name = "h11", specifier = ">=0.16.0" },
|
||||
{ name = "httpx", extras = ["socks"], specifier = ">=0.28.1,<1.0" },
|
||||
{ name = "importlib-metadata", specifier = ">=8.0.0,<9.0" },
|
||||
{ name = "setuptools", specifier = ">=83.0.0" },
|
||||
{ name = "wheel", specifier = ">=0.46.2" },
|
||||
]
|
||||
@@ -939,7 +941,7 @@ wheels = [
|
||||
standard-no-fastapi-cloud-cli = [
|
||||
{ name = "email-validator" },
|
||||
{ name = "fastapi-cli", extra = ["standard-no-fastapi-cloud-cli"] },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx", extra = ["socks"] },
|
||||
{ name = "jinja2" },
|
||||
{ name = "pydantic-extra-types" },
|
||||
{ name = "pydantic-settings" },
|
||||
@@ -1341,18 +1343,17 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.25.2"
|
||||
version = "0.28.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "certifi" },
|
||||
{ name = "httpcore" },
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/23/911d93a022979d3ea295f659fbe7edb07b3f4561a477e83b3a6d0e0c914e/httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8", size = 123889, upload-time = "2023-11-24T12:36:33.988Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/65/6940eeb21dcb2953778a6895281c179efd9100463ff08cb6232bb6480da7/httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118", size = 74980, upload-time = "2023-11-24T12:36:31.403Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -1368,7 +1369,7 @@ dependencies = [
|
||||
{ name = "filelock" },
|
||||
{ name = "fsspec" },
|
||||
{ name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx", extra = ["socks"] },
|
||||
{ name = "packaging" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "tqdm" },
|
||||
@@ -1391,14 +1392,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "6.11.0"
|
||||
version = "8.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "zipp" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/eb/58c2ab27ee628ad801f56d4017fe62afab0293116f6d0b08f1d5bd46e06f/importlib_metadata-6.11.0.tar.gz", hash = "sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443", size = 54593, upload-time = "2023-12-03T17:33:10.693Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/72/c600ae4f68c28fc19f9c31b9403053e5dbb8cace2e6842c7b7c3e4d42fe9/importlib_metadata-8.9.0.tar.gz", hash = "sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee", size = 56140, upload-time = "2026-03-20T16:56:26.362Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/9b/ecce94952ab5ea74c31dcf9ccf78ccd484eebebef06019bf8cb579ab4519/importlib_metadata-6.11.0-py3-none-any.whl", hash = "sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b", size = 23427, upload-time = "2023-12-03T17:33:08.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/f9/97f2ca8bb3ec6e4b1d64f983ebe98b9a192faddff67fac3d6303a537e670/importlib_metadata-8.9.0-py3-none-any.whl", hash = "sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f", size = 27220, upload-time = "2026-03-20T16:56:25.07Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1525,13 +1526,13 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "litellm"
|
||||
version = "1.83.0"
|
||||
version = "1.84.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
{ name = "click" },
|
||||
{ name = "fastuuid" },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx", extra = ["socks"] },
|
||||
{ name = "importlib-metadata" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "jsonschema" },
|
||||
@@ -1541,9 +1542,9 @@ dependencies = [
|
||||
{ name = "tiktoken" },
|
||||
{ name = "tokenizers" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/92/6ce9737554994ca8e536e5f4f6a87cc7c4774b656c9eb9add071caf7d54b/litellm-1.83.0.tar.gz", hash = "sha256:860bebc76c4bb27b4cf90b4a77acd66dba25aced37e3db98750de8a1766bfb7a", size = 17333062, upload-time = "2026-03-31T05:08:25.331Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/c4/512c8cb204450b585bb7bee2cef9466c8b79b90cf774766f319de5c444ed/litellm-1.84.10.tar.gz", hash = "sha256:5ccb6aec803c35f463a7ea1a446030fe99f7c556388b435dc2fb7ad91aa48a24", size = 15123874, upload-time = "2026-06-24T03:57:19.791Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/2c/a670cc050fcd6f45c6199eb99e259c73aea92edba8d5c2fc1b3686d36217/litellm-1.83.0-py3-none-any.whl", hash = "sha256:88c536d339248f3987571493015784671ba3f193a328e1ea6780dbebaa2094a8", size = 15610306, upload-time = "2026-03-31T05:08:21.987Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/88/e45bcdefc7a85bbef8eb852111dd4e02b92ea25727b6009c78893a768deb/litellm-1.84.10-py3-none-any.whl", hash = "sha256:7e175ebec04aa92149794adc83e4dd82b60d2b833c1ec265d68c08e8f56edde5", size = 16753091, upload-time = "2026-06-24T03:57:16.759Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1825,7 +1826,7 @@ source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "distro" },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx", extra = ["socks"] },
|
||||
{ name = "jiter" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "sniffio" },
|
||||
@@ -2579,7 +2580,7 @@ dependencies = [
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "aiohttp" },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx", extra = ["socks"] },
|
||||
{ name = "mypy" },
|
||||
{ name = "openai" },
|
||||
{ name = "psutil" },
|
||||
@@ -2599,8 +2600,8 @@ requires-dist = [
|
||||
{ name = "fastapi", extras = ["standard-no-fastapi-cloud-cli"], specifier = ">=0.141" },
|
||||
{ name = "greenlet", specifier = ">=3.2.1" },
|
||||
{ name = "h11", specifier = ">=0.16" },
|
||||
{ name = "httpx", extras = ["socks"], specifier = ">=0.25.2" },
|
||||
{ name = "litellm", specifier = ">=1.55.0" },
|
||||
{ name = "httpx", extras = ["socks"], specifier = ">=0.28.1" },
|
||||
{ name = "litellm", specifier = ">=1.84.0,<1.85" },
|
||||
{ name = "marshmallow", specifier = ">=3.13,<4.0" },
|
||||
{ name = "mdurl", specifier = "==0.1.2" },
|
||||
{ name = "nostr", specifier = ">=0.0.2" },
|
||||
@@ -2842,6 +2843,7 @@ version = "2.0.42"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
|
||||
{ name = "importlib-metadata" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/03/a0af991e3a43174d6b83fca4fb399745abceddd1171bdabae48ce877ff47/sqlalchemy-2.0.42.tar.gz", hash = "sha256:160bedd8a5c28765bd5be4dec2d881e109e33b34922e50a3b881a7681773ac5f", size = 9749972, upload-time = "2025-07-29T12:48:09.323Z" }
|
||||
|
||||
Reference in New Issue
Block a user