mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-26 17:25:10 +00:00
fix(security): validate URLs before network requests in arxiv tool and devtools
Apply validate_url() before fetching the arXiv API and PyPI polling endpoints. Replace urllib.request.urlopen with requests.get to satisfy bandit S310 without noqa suppressions. Update arxiv tool tests to mock requests.get and validate_url. Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,9 @@ import xml.etree.ElementTree as ET
|
|||||||
|
|
||||||
from crewai.tools import BaseTool, EnvVar
|
from crewai.tools import BaseTool, EnvVar
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from crewai_tools.security.safe_path import validate_url
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__file__)
|
logger = logging.getLogger(__file__)
|
||||||
@@ -78,17 +81,17 @@ class ArxivPaperTool(BaseTool):
|
|||||||
def fetch_arxiv_data(
|
def fetch_arxiv_data(
|
||||||
self, search_query: str, max_results: int
|
self, search_query: str, max_results: int
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
api_url = f"{self.BASE_API_URL}?search_query={urllib.parse.quote(search_query)}&start=0&max_results={max_results}"
|
api_url = validate_url(
|
||||||
|
f"{self.BASE_API_URL}?search_query={urllib.parse.quote(search_query)}"
|
||||||
|
f"&start=0&max_results={max_results}"
|
||||||
|
)
|
||||||
logger.info(f"Fetching data from Arxiv API: {api_url}")
|
logger.info(f"Fetching data from Arxiv API: {api_url}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen( # noqa: S310
|
response = requests.get(api_url, timeout=self.REQUEST_TIMEOUT)
|
||||||
api_url, timeout=self.REQUEST_TIMEOUT
|
response.raise_for_status()
|
||||||
) as response:
|
data = response.text
|
||||||
if response.status != 200:
|
except requests.RequestException as e:
|
||||||
raise Exception(f"HTTP {response.status}: {response.reason}")
|
|
||||||
data = response.read().decode("utf-8")
|
|
||||||
except urllib.error.URLError as e:
|
|
||||||
logger.error(f"Error fetching data from Arxiv: {e}")
|
logger.error(f"Error fetching data from Arxiv: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
import urllib.error
|
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from crewai_tools import ArxivPaperTool
|
from crewai_tools import ArxivPaperTool
|
||||||
import pytest
|
import pytest
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -12,6 +12,15 @@ def tool():
|
|||||||
return ArxivPaperTool(download_pdfs=False)
|
return ArxivPaperTool(download_pdfs=False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_validate_url():
|
||||||
|
with patch(
|
||||||
|
"crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.validate_url",
|
||||||
|
side_effect=lambda url: url,
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
def mock_arxiv_response():
|
def mock_arxiv_response():
|
||||||
return """<?xml version="1.0" encoding="UTF-8"?>
|
return """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
@@ -26,21 +35,23 @@ def mock_arxiv_response():
|
|||||||
</feed>"""
|
</feed>"""
|
||||||
|
|
||||||
|
|
||||||
@patch("urllib.request.urlopen")
|
@patch("crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.requests.get")
|
||||||
def test_fetch_arxiv_data(mock_urlopen, tool):
|
def test_fetch_arxiv_data(mock_get, tool):
|
||||||
mock_response = MagicMock()
|
mock_response = MagicMock()
|
||||||
mock_response.status = 200
|
mock_response.text = mock_arxiv_response()
|
||||||
mock_response.read.return_value = mock_arxiv_response().encode("utf-8")
|
mock_get.return_value = mock_response
|
||||||
mock_urlopen.return_value.__enter__.return_value = mock_response
|
|
||||||
|
|
||||||
results = tool.fetch_arxiv_data("transformer", 1)
|
results = tool.fetch_arxiv_data("transformer", 1)
|
||||||
assert isinstance(results, list)
|
assert isinstance(results, list)
|
||||||
assert results[0]["title"] == "Sample Paper"
|
assert results[0]["title"] == "Sample Paper"
|
||||||
|
|
||||||
|
|
||||||
@patch("urllib.request.urlopen", side_effect=urllib.error.URLError("Timeout"))
|
@patch(
|
||||||
def test_fetch_arxiv_data_network_error(mock_urlopen, tool):
|
"crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.requests.get",
|
||||||
with pytest.raises(urllib.error.URLError):
|
side_effect=requests.RequestException("Timeout"),
|
||||||
|
)
|
||||||
|
def test_fetch_arxiv_data_network_error(mock_get, tool):
|
||||||
|
with pytest.raises(requests.RequestException):
|
||||||
tool.fetch_arxiv_data("transformer", 1)
|
tool.fetch_arxiv_data("transformer", 1)
|
||||||
|
|
||||||
|
|
||||||
@@ -60,13 +71,12 @@ def test_download_pdf_oserror(mock_urlretrieve):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@patch("urllib.request.urlopen")
|
@patch("crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.requests.get")
|
||||||
@patch("urllib.request.urlretrieve")
|
@patch("urllib.request.urlretrieve")
|
||||||
def test_run_with_download(mock_urlretrieve, mock_urlopen):
|
def test_run_with_download(mock_urlretrieve, mock_get):
|
||||||
mock_response = MagicMock()
|
mock_response = MagicMock()
|
||||||
mock_response.status = 200
|
mock_response.text = mock_arxiv_response()
|
||||||
mock_response.read.return_value = mock_arxiv_response().encode("utf-8")
|
mock_get.return_value = mock_response
|
||||||
mock_urlopen.return_value.__enter__.return_value = mock_response
|
|
||||||
|
|
||||||
tool = ArxivPaperTool(download_pdfs=True)
|
tool = ArxivPaperTool(download_pdfs=True)
|
||||||
output = tool._run("transformer", 1)
|
output = tool._run("transformer", 1)
|
||||||
@@ -74,12 +84,11 @@ def test_run_with_download(mock_urlretrieve, mock_urlopen):
|
|||||||
mock_urlretrieve.assert_called_once()
|
mock_urlretrieve.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
@patch("urllib.request.urlopen")
|
@patch("crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.requests.get")
|
||||||
def test_run_no_download(mock_urlopen):
|
def test_run_no_download(mock_get):
|
||||||
mock_response = MagicMock()
|
mock_response = MagicMock()
|
||||||
mock_response.status = 200
|
mock_response.text = mock_arxiv_response()
|
||||||
mock_response.read.return_value = mock_arxiv_response().encode("utf-8")
|
mock_get.return_value = mock_response
|
||||||
mock_urlopen.return_value.__enter__.return_value = mock_response
|
|
||||||
|
|
||||||
tool = ArxivPaperTool(download_pdfs=False)
|
tool = ArxivPaperTool(download_pdfs=False)
|
||||||
result = tool._run("transformer", 1)
|
result = tool._run("transformer", 1)
|
||||||
@@ -93,20 +102,19 @@ def test_validate_save_path_creates_directory(mock_mkdir):
|
|||||||
assert isinstance(path, Path)
|
assert isinstance(path, Path)
|
||||||
|
|
||||||
|
|
||||||
@patch("urllib.request.urlopen")
|
@patch("crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.requests.get")
|
||||||
def test_run_handles_exception(mock_urlopen):
|
def test_run_handles_exception(mock_get):
|
||||||
mock_urlopen.side_effect = Exception("API failure")
|
mock_get.side_effect = Exception("API failure")
|
||||||
tool = ArxivPaperTool()
|
tool = ArxivPaperTool()
|
||||||
result = tool._run("transformer", 1)
|
result = tool._run("transformer", 1)
|
||||||
assert "Failed to fetch or download Arxiv papers" in result
|
assert "Failed to fetch or download Arxiv papers" in result
|
||||||
|
|
||||||
|
|
||||||
@patch("urllib.request.urlopen")
|
@patch("crewai_tools.tools.arxiv_paper_tool.arxiv_paper_tool.requests.get")
|
||||||
def test_invalid_xml_response(mock_urlopen, tool):
|
def test_invalid_xml_response(mock_get, tool):
|
||||||
mock_response = MagicMock()
|
mock_response = MagicMock()
|
||||||
mock_response.read.return_value = b"<invalid><xml>"
|
mock_response.text = "<invalid><xml>"
|
||||||
mock_response.status = 200
|
mock_get.return_value = mock_response
|
||||||
mock_urlopen.return_value.__enter__.return_value = mock_response
|
|
||||||
|
|
||||||
with pytest.raises(ET.ParseError):
|
with pytest.raises(ET.ParseError):
|
||||||
tool.fetch_arxiv_data("quantum", 1)
|
tool.fetch_arxiv_data("quantum", 1)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ dependencies = [
|
|||||||
"python-dotenv>=1.2.2,<2",
|
"python-dotenv>=1.2.2,<2",
|
||||||
"pygithub~=1.59.1",
|
"pygithub~=1.59.1",
|
||||||
"rich>=13.9.4",
|
"rich>=13.9.4",
|
||||||
|
"crewai-tools",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import sys
|
|||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
from typing import Final, Literal
|
from typing import Final, Literal
|
||||||
from urllib.request import urlopen
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from crewai_tools.security.safe_path import validate_url
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from github import Github
|
from github import Github
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
|
import requests
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import Markdown
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
@@ -1548,14 +1549,14 @@ def _wait_for_pypi(package: str, version: str) -> None:
|
|||||||
package: PyPI package name.
|
package: PyPI package name.
|
||||||
version: Version string to wait for.
|
version: Version string to wait for.
|
||||||
"""
|
"""
|
||||||
url = f"https://pypi.org/pypi/{package}/{version}/json"
|
url = validate_url(f"https://pypi.org/pypi/{package}/{version}/json")
|
||||||
deadline = time.monotonic() + _PYPI_POLL_TIMEOUT
|
deadline = time.monotonic() + _PYPI_POLL_TIMEOUT
|
||||||
|
|
||||||
console.print(f"[cyan]Waiting for {package}=={version} to appear on PyPI...[/cyan]")
|
console.print(f"[cyan]Waiting for {package}=={version} to appear on PyPI...[/cyan]")
|
||||||
while time.monotonic() < deadline:
|
while time.monotonic() < deadline:
|
||||||
try:
|
try:
|
||||||
with urlopen(url) as resp: # noqa: S310
|
response = requests.get(url, timeout=30)
|
||||||
if resp.status == 200:
|
if response.status_code == 200:
|
||||||
console.print(
|
console.print(
|
||||||
f"[green]✓[/green] {package}=={version} is available on PyPI"
|
f"[green]✓[/green] {package}=={version} is available on PyPI"
|
||||||
)
|
)
|
||||||
|
|||||||
98
uv.lock
generated
98
uv.lock
generated
@@ -13,7 +13,7 @@ resolution-markers = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
exclude-newer = "2026-07-04T15:35:51.457693Z"
|
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
|
||||||
exclude-newer-span = "P3D"
|
exclude-newer-span = "P3D"
|
||||||
|
|
||||||
[options.exclude-newer-package]
|
[options.exclude-newer-package]
|
||||||
@@ -1052,7 +1052,7 @@ name = "coloredlogs"
|
|||||||
version = "15.0.1"
|
version = "15.0.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "humanfriendly", marker = "python_full_version < '3.11'" },
|
{ name = "humanfriendly" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -1150,7 +1150,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and platform_machine == 's390x'",
|
"python_full_version < '3.11' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -1225,7 +1225,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and platform_machine == 's390x'",
|
"python_full_version == '3.11.*' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -1557,6 +1557,7 @@ name = "crewai-devtools"
|
|||||||
source = { editable = "lib/devtools" }
|
source = { editable = "lib/devtools" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
|
{ name = "crewai-tools" },
|
||||||
{ name = "openai" },
|
{ name = "openai" },
|
||||||
{ name = "pygithub" },
|
{ name = "pygithub" },
|
||||||
{ name = "python-dotenv" },
|
{ name = "python-dotenv" },
|
||||||
@@ -1567,6 +1568,7 @@ dependencies = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "click", specifier = ">=8.1.7,<9" },
|
{ name = "click", specifier = ">=8.1.7,<9" },
|
||||||
|
{ name = "crewai-tools", editable = "lib/crewai-tools" },
|
||||||
{ name = "openai", specifier = ">=1.83.0,<3" },
|
{ name = "openai", specifier = ">=1.83.0,<3" },
|
||||||
{ name = "pygithub", specifier = "~=1.59.1" },
|
{ name = "pygithub", specifier = "~=1.59.1" },
|
||||||
{ name = "python-dotenv", specifier = ">=1.2.2,<2" },
|
{ name = "python-dotenv", specifier = ">=1.2.2,<2" },
|
||||||
@@ -1877,34 +1879,34 @@ wheels = [
|
|||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
cudart = [
|
cudart = [
|
||||||
{ name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-cuda-runtime" },
|
||||||
]
|
]
|
||||||
cufft = [
|
cufft = [
|
||||||
{ name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-cufft" },
|
||||||
]
|
]
|
||||||
cufile = [
|
cufile = [
|
||||||
{ name = "nvidia-cufile", marker = "sys_platform == 'linux'" },
|
{ name = "nvidia-cufile" },
|
||||||
]
|
]
|
||||||
cupti = [
|
cupti = [
|
||||||
{ name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-cuda-cupti" },
|
||||||
]
|
]
|
||||||
curand = [
|
curand = [
|
||||||
{ name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-curand" },
|
||||||
]
|
]
|
||||||
cusolver = [
|
cusolver = [
|
||||||
{ name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-cusolver" },
|
||||||
]
|
]
|
||||||
cusparse = [
|
cusparse = [
|
||||||
{ name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-cusparse" },
|
||||||
]
|
]
|
||||||
nvjitlink = [
|
nvjitlink = [
|
||||||
{ name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-nvjitlink" },
|
||||||
]
|
]
|
||||||
nvrtc = [
|
nvrtc = [
|
||||||
{ name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-cuda-nvrtc" },
|
||||||
]
|
]
|
||||||
nvtx = [
|
nvtx = [
|
||||||
{ name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
{ name = "nvidia-nvtx" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2429,7 +2431,7 @@ name = "exceptiongroup"
|
|||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -3018,8 +3020,8 @@ name = "grpcio-health-checking"
|
|||||||
version = "1.71.2"
|
version = "1.71.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "grpcio", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "grpcio" },
|
||||||
{ name = "protobuf", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "protobuf" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/53/86/20994347ef36b7626fb74539f13128100dd8b7eaac67efc063264e6cdc80/grpcio_health_checking-1.71.2.tar.gz", hash = "sha256:1c21ece88c641932f432b573ef504b20603bdf030ad4e1ec35dd7fdb4ea02637", size = 16770, upload-time = "2025-06-28T04:24:08.768Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/53/86/20994347ef36b7626fb74539f13128100dd8b7eaac67efc063264e6cdc80/grpcio_health_checking-1.71.2.tar.gz", hash = "sha256:1c21ece88c641932f432b573ef504b20603bdf030ad4e1ec35dd7fdb4ea02637", size = 16770, upload-time = "2025-06-28T04:24:08.768Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -3223,7 +3225,7 @@ name = "humanfriendly"
|
|||||||
version = "10.0"
|
version = "10.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pyreadline3", marker = "python_full_version < '3.11' and sys_platform == 'win32'" },
|
{ name = "pyreadline3", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -4460,10 +4462,10 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and platform_machine == 's390x'",
|
"python_full_version < '3.11' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jsonref", marker = "python_full_version < '3.12'" },
|
{ name = "jsonref" },
|
||||||
{ name = "mcp", extra = ["ws"], marker = "python_full_version < '3.12'" },
|
{ name = "mcp", extra = ["ws"] },
|
||||||
{ name = "pydantic", marker = "python_full_version < '3.12'" },
|
{ name = "pydantic" },
|
||||||
{ name = "python-dotenv", marker = "python_full_version < '3.12'" },
|
{ name = "python-dotenv" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/28/64fc666fa5d86bb1b048c167975d4ea19210f9f8571b64b26563739774ac/mcpadapt-0.1.19.tar.gz", hash = "sha256:dfab84fc75cc84a49a40bd61079773b1faf840227b74b82c71a7755b9c1957c5", size = 4227721, upload-time = "2025-10-16T07:11:56.736Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/28/64fc666fa5d86bb1b048c167975d4ea19210f9f8571b64b26563739774ac/mcpadapt-0.1.19.tar.gz", hash = "sha256:dfab84fc75cc84a49a40bd61079773b1faf840227b74b82c71a7755b9c1957c5", size = 4227721, upload-time = "2025-10-16T07:11:56.736Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -4481,10 +4483,10 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.12.*' and platform_machine == 's390x'",
|
"python_full_version == '3.12.*' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jsonref", marker = "python_full_version >= '3.12'" },
|
{ name = "jsonref" },
|
||||||
{ name = "mcp", extra = ["ws"], marker = "python_full_version >= '3.12'" },
|
{ name = "mcp", extra = ["ws"] },
|
||||||
{ name = "pydantic", marker = "python_full_version >= '3.12'" },
|
{ name = "pydantic" },
|
||||||
{ name = "python-dotenv", marker = "python_full_version >= '3.12'" },
|
{ name = "python-dotenv" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e3/71/1bbbe157e55d30ab4a74fa878f6942cc0586e9820f03e03451a3d2297e9b/mcpadapt-0.1.20.tar.gz", hash = "sha256:4047c0da61e481dd0673a48936a427da9e6547c6cf0d580ff4e4761dcf058ed1", size = 4203656, upload-time = "2025-10-24T15:35:02.135Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/e3/71/1bbbe157e55d30ab4a74fa878f6942cc0586e9820f03e03451a3d2297e9b/mcpadapt-0.1.20.tar.gz", hash = "sha256:4047c0da61e481dd0673a48936a427da9e6547c6cf0d580ff4e4761dcf058ed1", size = 4203656, upload-time = "2025-10-24T15:35:02.135Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -5499,12 +5501,12 @@ name = "onnxruntime"
|
|||||||
version = "1.23.2"
|
version = "1.23.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "coloredlogs", marker = "python_full_version < '3.11'" },
|
{ name = "coloredlogs" },
|
||||||
{ name = "flatbuffers", marker = "python_full_version < '3.11'" },
|
{ name = "flatbuffers" },
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
||||||
{ name = "packaging", marker = "python_full_version < '3.11'" },
|
{ name = "packaging" },
|
||||||
{ name = "protobuf", marker = "python_full_version < '3.11'" },
|
{ name = "protobuf" },
|
||||||
{ name = "sympy", marker = "python_full_version < '3.11'" },
|
{ name = "sympy" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/35/d6/311b1afea060015b56c742f3531168c1644650767f27ef40062569960587/onnxruntime-1.23.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:a7730122afe186a784660f6ec5807138bf9d792fa1df76556b27307ea9ebcbe3", size = 17195934, upload-time = "2025-10-27T23:06:14.143Z" },
|
{ url = "https://files.pythonhosted.org/packages/35/d6/311b1afea060015b56c742f3531168c1644650767f27ef40062569960587/onnxruntime-1.23.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:a7730122afe186a784660f6ec5807138bf9d792fa1df76556b27307ea9ebcbe3", size = 17195934, upload-time = "2025-10-27T23:06:14.143Z" },
|
||||||
@@ -8173,7 +8175,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and platform_machine == 's390x'",
|
"python_full_version < '3.11' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -8237,7 +8239,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and platform_machine == 's390x'",
|
"python_full_version == '3.11.*' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -9868,13 +9870,13 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and platform_machine == 's390x'",
|
"python_full_version < '3.11' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "authlib", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "authlib" },
|
||||||
{ name = "deprecation", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "deprecation" },
|
||||||
{ name = "grpcio", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "grpcio" },
|
||||||
{ name = "grpcio-health-checking", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "grpcio-health-checking" },
|
||||||
{ name = "httpx", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "httpx" },
|
||||||
{ name = "pydantic", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "pydantic" },
|
||||||
{ name = "validators", marker = "python_full_version < '3.11' or (python_full_version >= '3.13' and platform_machine != 's390x')" },
|
{ name = "validators" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/b9/7b9e05cf923743aa1479afcd85c48ebca82d031c3c3a5d02b1b3fcb52eb9/weaviate_client-4.16.2.tar.gz", hash = "sha256:eb7107a3221a5ad68d604cafc65195bd925a9709512ea0b6fe0dd212b0678fab", size = 681321, upload-time = "2025-07-22T09:10:48.79Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/a7/b9/7b9e05cf923743aa1479afcd85c48ebca82d031c3c3a5d02b1b3fcb52eb9/weaviate_client-4.16.2.tar.gz", hash = "sha256:eb7107a3221a5ad68d604cafc65195bd925a9709512ea0b6fe0dd212b0678fab", size = 681321, upload-time = "2025-07-22T09:10:48.79Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -9893,13 +9895,13 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and platform_machine == 's390x'",
|
"python_full_version == '3.11.*' and platform_machine == 's390x'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "authlib", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "authlib" },
|
||||||
{ name = "grpcio", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "grpcio" },
|
||||||
{ name = "httpx", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "httpx" },
|
||||||
{ name = "packaging", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "packaging" },
|
||||||
{ name = "protobuf", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "protobuf" },
|
||||||
{ name = "pydantic", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "pydantic" },
|
||||||
{ name = "validators", marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and platform_machine == 's390x')" },
|
{ name = "validators" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2b/b8/103f3aaa246d4e932f4cfeb846e51436966f2aeedf60c2665a3fc51a975a/weaviate_client-4.21.3.tar.gz", hash = "sha256:d7b1f2b0cecbc747e9427f4e3b9463cdfee090746bfbbd40e59cfa25ea2afd4a", size = 847895, upload-time = "2026-06-02T13:03:51.598Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/2b/b8/103f3aaa246d4e932f4cfeb846e51436966f2aeedf60c2665a3fc51a975a/weaviate_client-4.21.3.tar.gz", hash = "sha256:d7b1f2b0cecbc747e9427f4e3b9463cdfee090746bfbbd40e59cfa25ea2afd4a", size = 847895, upload-time = "2026-06-02T13:03:51.598Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
|
|||||||
Reference in New Issue
Block a user