├── .python-version ├── wavespeed_mcp ├── __init__.py ├── exceptions.py ├── const.py ├── __main__.py ├── client.py ├── utils.py └── server.py ├── setup.py ├── .env.example ├── script └── build.sh ├── wavespeed_mcp_config_demo.json ├── .gitignore ├── pyproject.toml ├── README.zh.md ├── tests ├── test_integration.py └── test_wavespeed_mcp.py ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /wavespeed_mcp/__init__.py: -------------------------------------------------------------------------------- 1 | """WavespeedMCP package.""" 2 | 3 | __version__ = "0.1.2" 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | packages=find_packages(), 5 | include_package_data=True, 6 | ) 7 | -------------------------------------------------------------------------------- /wavespeed_mcp/exceptions.py: -------------------------------------------------------------------------------- 1 | """Custom exceptions for WavespeedMCP.""" 2 | 3 | 4 | class WavespeedAPIError(Exception): 5 | """Base exception for Wavespeed API errors.""" 6 | pass 7 | 8 | 9 | class WavespeedAuthError(WavespeedAPIError): 10 | """Authentication related errors.""" 11 | pass 12 | 13 | 14 | class WavespeedRequestError(WavespeedAPIError): 15 | """Request related errors.""" 16 | pass 17 | 18 | 19 | class WavespeedTimeoutError(WavespeedAPIError): 20 | """Timeout related errors.""" 21 | pass 22 | 23 | 24 | class WavespeedValidationError(WavespeedAPIError): 25 | """Validation related errors.""" 26 | pass 27 | 28 | 29 | class WavespeedMcpError(WavespeedAPIError): 30 | """General MCP related errors.""" 31 | pass 32 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # WaveSpeed API Configuration 2 | WAVESPEED_API_KEY=your_api_key_here 3 | WAVESPEED_API_HOST=https://api.wavespeed.ai 4 | 5 | # MCP Configuration 6 | WAVESPEED_MCP_BASE_PATH=~/Desktop 7 | WAVESPEED_API_RESOURCE_MODE=url 8 | 9 | # Logging Configuration 10 | WAVESPEED_LOG_LEVEL=INFO 11 | # Optional: Specify log file path (if not set, logs to console) 12 | # WAVESPEED_LOG_FILE=/tmp/wavespeed-mcp.log 13 | # WAVESPEED_LOG_FILE=/var/log/wavespeed-mcp.log 14 | # WAVESPEED_LOG_FILE=~/logs/wavespeed-mcp.log 15 | 16 | # Network Configuration 17 | # Request timeout per HTTP call in seconds (default: 300 = 5 minutes) 18 | # WAVESPEED_REQUEST_TIMEOUT=300 19 | 20 | # Total timeout for waiting/polling results in seconds (default: 600 = 10 minutes) 21 | # WAVESPEED_WAIT_RESULT_TIMEOUT=600 -------------------------------------------------------------------------------- /script/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # change to project root 6 | cd "$(dirname "$0")/.." || exit 1 7 | 8 | # show current directory and version 9 | echo "current directory: $(pwd)" 10 | VERSION=$(grep -m 1 "version =" pyproject.toml | cut -d '"' -f2) 11 | echo "build version: $VERSION" 12 | 13 | # clean old build files 14 | echo "clean old build files..." 15 | rm -rf dist/ build/ *.egg-info/ 16 | 17 | # install build tools 18 | echo "install/upgrade build tools..." 19 | pip install --upgrade build twine 20 | 21 | # build package 22 | echo "build distribution package..." 23 | python -m build 24 | 25 | # list built files 26 | echo "built files:" 27 | ls -l dist/ 28 | 29 | # confirm upload 30 | read -p "upload to PyPI? (y/n): " UPLOAD 31 | if [[ "$UPLOAD" == "y" || "$UPLOAD" == "Y" ]]; then 32 | echo "upload to PyPI..." 33 | python -m twine upload dist/* 34 | echo "upload completed!" 35 | else 36 | echo "skip upload." 37 | fi 38 | 39 | echo "build process completed." 40 | -------------------------------------------------------------------------------- /wavespeed_mcp_config_demo.json: -------------------------------------------------------------------------------- 1 | { 2 | "// MCP 服务器配置 - 用于 Claude Desktop 集成": "", 3 | "mcpServers": { 4 | "WaveSpeed": { 5 | "command": "wavespeed-mcp", 6 | "env": { 7 | "WAVESPEED_API_KEY": "your_api_key_here", 8 | "WAVESPEED_API_HOST": "https://api.wavespeed.ai", 9 | "WAVESPEED_MCP_BASE_PATH": "~/Desktop", 10 | "WAVESPEED_API_RESOURCE_MODE": "url", 11 | "WAVESPEED_LOG_LEVEL": "INFO" 12 | } 13 | } 14 | }, 15 | 16 | "// WaveSpeed MCP 服务器配置": "", 17 | "api": { 18 | "key": "your_wavespeed_api_key_here", 19 | "host": "https://api.wavespeed.ai", 20 | "timeout": 60, 21 | "max_retries": -1, 22 | "retry_delay": 1, 23 | "polling_interval": 2, 24 | "endpoints": { 25 | "text_to_image": "/wavespeed-ai/flux-dev", 26 | "image_to_image": "/wavespeed-ai/flux-kontext-pro", 27 | "video": "/wavespeed-ai/wan-2.1/i2v-480p-lora" 28 | } 29 | }, 30 | "output": { 31 | "base_path": "~/Desktop", 32 | "resource_mode": "url" 33 | }, 34 | "logging": { 35 | "level": "INFO" 36 | }, 37 | "telemetry": { 38 | "enabled": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | MANIFEST 23 | 24 | # Virtual Environment 25 | venv/ 26 | env/ 27 | ENV/ 28 | .env 29 | 30 | # IDE and Editors 31 | .idea/ 32 | .vscode/ 33 | *.swp 34 | *.swo 35 | .DS_Store 36 | .project 37 | .pydevproject 38 | .settings/ 39 | 40 | # Logs 41 | logs/ 42 | *.log 43 | npm-debug.log* 44 | yarn-debug.log* 45 | yarn-error.log* 46 | 47 | # Database 48 | *.sqlite3 49 | *.db 50 | 51 | # Local configuration 52 | config.local.yaml 53 | config.local.yml 54 | config.local.json 55 | .env.local 56 | claude_desktop_config.json 57 | 58 | # Testing 59 | .coverage 60 | htmlcov/ 61 | .pytest_cache/ 62 | .tox/ 63 | nosetests.xml 64 | coverage.xml 65 | *.cover 66 | 67 | # Documentation 68 | docs/_build/ 69 | site/ 70 | 71 | # Dependency directories 72 | node_modules/ 73 | jspm_packages/ 74 | 75 | # Distribution / packaging 76 | .Python 77 | env/ 78 | build/ 79 | develop-eggs/ 80 | dist/ 81 | downloads/ 82 | eggs/ 83 | .eggs/ 84 | lib/ 85 | lib64/ 86 | parts/ 87 | sdist/ 88 | var/ 89 | wheels/ 90 | *.egg-info/ 91 | .installed.cfg 92 | *.egg 93 | 94 | # Jupyter Notebook 95 | .ipynb_checkpoints 96 | 97 | # mypy 98 | .mypy_cache/ 99 | 100 | # Temporary files 101 | *.tmp 102 | *.bak 103 | *.swp 104 | *~.nib 105 | example/ -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "wavespeed-mcp" 3 | version = "0.1.27" 4 | description = "WaveSpeed MCP Server" 5 | authors = [ 6 | { name = "Wavespeed AI Team", email = "support@wavespeed.ai" }, 7 | ] 8 | readme = "README.md" 9 | license = { file = "LICENSE" } 10 | classifiers = [ 11 | "Development Status :: 4 - Beta", 12 | "Intended Audience :: Developers", 13 | "License :: OSI Approved :: MIT License", 14 | "Programming Language :: Python :: 3", 15 | "Programming Language :: Python :: 3.11", 16 | "Programming Language :: Python :: 3.12", 17 | ] 18 | keywords = [ 19 | "wavespeed", 20 | "mcp", 21 | "image-generation", 22 | "video-generation", 23 | ] 24 | requires-python = ">=3.11" 25 | dependencies = [ 26 | "mcp[cli]>=1.6.0", 27 | "fastapi==0.109.2", 28 | "uvicorn==0.27.1", 29 | "python-dotenv==1.0.1", 30 | "pydantic>=2.6.1", 31 | "httpx==0.28.1", 32 | "requests==2.31.0", 33 | "tqdm>=4.66.0", 34 | "setuptools>=80.9.0", 35 | ] 36 | 37 | [project.scripts] 38 | wavespeed-mcp = "wavespeed_mcp.server:main" 39 | 40 | [project.optional-dependencies] 41 | dev = [ 42 | "pre-commit==3.6.2", 43 | "ruff==0.3.0", 44 | "fastmcp==0.4.1", 45 | "pytest==8.0.0", 46 | "pytest-cov==4.1.0", 47 | "twine==6.1.0", 48 | "build>=1.0.3", 49 | ] 50 | 51 | [build-system] 52 | requires = ["setuptools>=45", "wheel"] 53 | build-backend = "setuptools.build_meta" 54 | 55 | [tool.pytest.ini_options] 56 | testpaths = ["tests"] 57 | python_files = ["test_*.py"] 58 | addopts = "-v --cov=wavespeed_mcp --cov-report=term-missing" 59 | -------------------------------------------------------------------------------- /wavespeed_mcp/const.py: -------------------------------------------------------------------------------- 1 | """ 2 | Constants for WavespeedMCP. 3 | """ 4 | 5 | # Image generation default values 6 | DEFAULT_IMAGE_SIZE = "1024*1024" 7 | DEFAULT_NUM_INFERENCE_STEPS = 28 8 | DEFAULT_GUIDANCE_SCALE = 3.5 9 | DEFAULT_NUM_IMAGES = 1 10 | DEFAULT_SEED = -1 11 | DEFAULT_STRENGTH = 0.8 12 | DEFAULT_IMAGE_LORA = {"path": "linoyts/yarn_art_Flux_LoRA", "scale": 1.0} 13 | 14 | # Video generation default values 15 | DEFAULT_VIDEO_SIZE = "832*480" 16 | DEFAULT_VIDEO_DURATION = 5 17 | DEFAULT_VIDEO_GUIDANCE_SCALE = 5 18 | DEFAULT_VIDEO_FLOW_SHIFT = 3 19 | DEFAULT_VIDEO_NUM_INFERENCE_STEPS = 30 20 | DEFAULT_VIDEO_LORA = {"path": "Remade-AI/Deflate", "scale": 1.0} 21 | 22 | # Environment variables 23 | ENV_WAVESPEED_API_KEY = "WAVESPEED_API_KEY" 24 | ENV_WAVESPEED_API_HOST = "WAVESPEED_API_HOST" 25 | ENV_WAVESPEED_MCP_BASE_PATH = "WAVESPEED_MCP_BASE_PATH" 26 | # Options: local, url (default) 27 | ENV_RESOURCE_MODE = "WAVESPEED_API_RESOURCE_MODE" 28 | 29 | # Resource modes 30 | RESOURCE_MODE_BASE64 = "base64" # return base64 encoded image 31 | RESOURCE_MODE_LOCAL = "local" # save resource to local file system 32 | RESOURCE_MODE_URL = "url" # provide resource url 33 | 34 | # API endpoints 35 | API_VERSION = "v3" 36 | API_BASE_PATH = "/api" 37 | API_IMAGE_ENDPOINT = "/wavespeed-ai/flux-dev" 38 | API_IMAGE_TO_IMAGE_ENDPOINT = "/wavespeed-ai/flux-kontext-pro" 39 | API_VIDEO_ENDPOINT = "/wavespeed-ai/wan-2.1/i2v-480p-lora" 40 | ENV_API_TEXT_TO_IMAGE_ENDPOINT = "WAVESPEED_API_TEXT_TO_IMAGE_ENDPOINT" 41 | ENV_API_IMAGE_TO_IMAGE_ENDPOINT = "WAVESPEED_API_IMAGE_TO_IMAGE_ENDPOINT" 42 | ENV_API_VIDEO_ENDPOINT = "WAVESPEED_API_VIDEO_ENDPOINT" 43 | API_PREDICTION_ENDPOINT = "/predictions" 44 | 45 | # Logging 46 | DEFAULT_LOG_LEVEL = "INFO" 47 | ENV_FASTMCP_LOG_LEVEL = "WAVESPEED_LOG_LEVEL" 48 | ENV_WAVESPEED_LOG_FILE = "WAVESPEED_LOG_FILE" 49 | 50 | # Network timeout (per HTTP request) 51 | DEFAULT_REQUEST_TIMEOUT = 300 # 5 minutes 52 | ENV_WAVESPEED_REQUEST_TIMEOUT = "WAVESPEED_REQUEST_TIMEOUT" 53 | 54 | # Polling timeout (total wait for result) 55 | DEFAULT_WAIT_RESULT_TIMEOUT = 600 # 10 minutes 56 | ENV_WAVESPEED_WAIT_RESULT_TIMEOUT = "WAVESPEED_WAIT_RESULT_TIMEOUT" 57 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # WavespeedMCP 2 | 3 | WavespeedMCP 是 WaveSpeed AI 服务的模型控制协议(MCP)服务器实现。它通过 MCP 协议为访问 WaveSpeed 的图像和视频生成功能提供了标准化接口。 4 | 5 | ## 功能特点 6 | 7 | - **高级图像生成**:从文本提示创建高质量图像,支持图像到图像生成、局部重绘和 LoRA 模型 8 | - **动态视频生成**:使用可自定义的动作参数将静态图像转换为视频 9 | - **优化性能**:通过智能重试逻辑和详细的进度跟踪增强 API 轮询 10 | - **灵活的资源处理**:支持 URL、Base64 和本地文件输出模式 11 | - **全面的错误处理**:专门的异常层次结构,用于精确识别和恢复错误 12 | - **强大的日志系统**:用于监控和调试的详细日志系统 13 | - **多种配置选项**:支持环境变量、命令行参数和配置文件 14 | 15 | ## 安装 16 | 17 | ### 前提条件 18 | 19 | - Python 3.11+ 20 | - WaveSpeed API 密钥(从 [WaveSpeed AI](https://wavespeed.ai) 获取) 21 | 22 | ### 安装步骤 23 | 24 | 直接从 PyPI 安装: 25 | 26 | ```bash 27 | pip install wavespeed-mcp 28 | ``` 29 | 30 | ### MCP 配置 31 | 32 | 要将 WavespeedMCP 与您的 IDE 或应用程序一起使用,请添加以下配置: 33 | 34 | ```json 35 | { 36 | "mcpServers": { 37 | "Wavespeed": { 38 | "command": "wavespeed-mcp", 39 | "env": { 40 | "WAVESPEED_API_KEY": "wavespeedkey" 41 | } 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | ## 使用方法 48 | 49 | ### 运行服务器 50 | 51 | 启动 WavespeedMCP 服务器: 52 | 53 | ```bash 54 | wavespeed-mcp --api-key your_api_key_here 55 | ``` 56 | 57 | ### Claude Desktop 集成 58 | 59 | WavespeedMCP 可以与 Claude Desktop 集成。要生成必要的配置文件: 60 | 61 | ```bash 62 | python -m wavespeed_mcp --api-key your_api_key_here --config-path /path/to/claude/config 63 | ``` 64 | 65 | 此命令生成一个 `claude_desktop_config.json` 文件,该文件配置 Claude Desktop 使用 WavespeedMCP 工具。生成配置后: 66 | 67 | 1. 使用 `wavespeed-mcp` 命令启动 WavespeedMCP 服务器 68 | 2. 启动 Claude Desktop,它将使用配置好的 WavespeedMCP 工具 69 | 70 | ## 配置选项 71 | 72 | WavespeedMCP 可以通过以下方式进行配置: 73 | 74 | 1. **环境变量**: 75 | 76 | - `WAVESPEED_API_KEY`:您的 WaveSpeed API 密钥(必需) 77 | - `WAVESPEED_API_HOST`:API 主机 URL(默认:https://api.wavespeed.ai) 78 | - `WAVESPEED_MCP_BASE_PATH`:输出文件的基本路径(默认:~/Desktop) 79 | - `WAVESPEED_API_RESOURCE_MODE`:资源输出模式(选项:url、base64、local;默认:url) 80 | - `WAVESPEED_LOG_LEVEL`:日志级别(选项:DEBUG、INFO、WARNING、ERROR;默认:INFO) 81 | - `WAVESPEED_API_TEXT_TO_IMAGE_ENDPOINT`:文本生成图像的自定义端点(默认:/wavespeed-ai/flux-dev) 82 | - `WAVESPEED_API_IMAGE_TO_IMAGE_ENDPOINT`:图像编辑的自定义端点(默认:/wavespeed-ai/flux-kontext-pro) 83 | - `WAVESPEED_API_VIDEO_ENDPOINT`:视频生成的自定义端点(默认:/wavespeed-ai/wan-2.1/i2v-480p-lora) 84 | 85 | 2. **命令行参数**: 86 | 87 | - `--api-key`:您的 WaveSpeed API 密钥 88 | - `--api-host`:API 主机 URL 89 | - `--config`:配置文件的路径 90 | 91 | 3. **配置文件**(JSON 格式): 92 | 参见 `wavespeed_mcp_config_demo.json` 示例。 93 | 94 | ## 架构 95 | 96 | WavespeedMCP 遵循清晰、模块化的架构: 97 | 98 | - `server.py`:核心 MCP 服务器实现,包含工具定义 99 | - `client.py`:优化的 API 客户端,具有智能轮询功能 100 | - `utils.py`:用于资源处理的综合实用函数 101 | - `exceptions.py`:用于错误处理的专门异常层次结构 102 | - `const.py`:常量和默认配置值 103 | 104 | ## 开发 105 | 106 | ### 要求 107 | 108 | - Python 3.11+ 109 | - 开发依赖:`pip install -e ".[dev]"` 110 | 111 | ### 测试 112 | 113 | 运行测试套件: 114 | 115 | ```bash 116 | pytest 117 | ``` 118 | 119 | 或者使用覆盖率报告: 120 | 121 | ```bash 122 | pytest --cov=wavespeed_mcp 123 | ``` 124 | 125 | ## 许可证 126 | 127 | 本项目根据 MIT 许可证授权 - 有关详细信息,请参阅 LICENSE 文件。 128 | 129 | ## 支持 130 | 131 | 如需支持或功能请求,请联系 WaveSpeed AI 团队:support@wavespeed.ai。 132 | -------------------------------------------------------------------------------- /wavespeed_mcp/__main__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from pathlib import Path 4 | import sys 5 | from dotenv import load_dotenv 6 | import argparse 7 | 8 | load_dotenv() 9 | 10 | 11 | def get_claude_config_path() -> Path | None: 12 | """Get the Claude config directory based on platform.""" 13 | if sys.platform == "win32": 14 | path = Path(Path.home(), "AppData", "Roaming", "Claude") 15 | elif sys.platform == "darwin": 16 | path = Path(Path.home(), "Library", "Application Support", "Claude") 17 | elif sys.platform.startswith("linux"): 18 | path = Path( 19 | os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"), "Claude" 20 | ) 21 | else: 22 | return None 23 | 24 | if path.exists(): 25 | return path 26 | return None 27 | 28 | 29 | def get_python_path(): 30 | return sys.executable 31 | 32 | 33 | def generate_config(api_key: str | None = None): 34 | """Generate Claude config file.""" 35 | final_api_key = api_key or os.environ.get("WAVESPEED_API_KEY") 36 | if not final_api_key: 37 | print("Error: WaveSpeed API key is required.") 38 | print("Please either:") 39 | print(" 1. Pass the API key using --api-key argument, or") 40 | print(" 2. Set the WAVESPEED_API_KEY environment variable, or") 41 | print(" 3. Add WAVESPEED_API_KEY to your .env file") 42 | sys.exit(1) 43 | 44 | config = { 45 | "mcpServers": { 46 | "WaveSpeed": { 47 | "command": "wavespeed-mcp", 48 | "env": { 49 | "WAVESPEED_API_KEY": final_api_key, 50 | "WAVESPEED_MCP_BASE_PATH": "", 51 | "WAVESPEED_API_HOST": "https://api.wavespeed.ai", 52 | }, 53 | } 54 | } 55 | } 56 | 57 | return config 58 | 59 | 60 | if __name__ == "__main__": 61 | parser = argparse.ArgumentParser() 62 | parser.add_argument( 63 | "--print", 64 | action="store_true", 65 | help="Print config to screen instead of writing to file", 66 | ) 67 | parser.add_argument( 68 | "--api-key", 69 | help="WaveSpeed API key (alternatively, set WAVESPEED_API_KEY environment variable)", 70 | ) 71 | parser.add_argument( 72 | "--config-path", 73 | type=Path, 74 | help="Custom path to Claude config directory", 75 | ) 76 | args = parser.parse_args() 77 | 78 | config = generate_config(args.api_key) 79 | 80 | if args.print: 81 | print(json.dumps(config, indent=2)) 82 | else: 83 | claude_path = args.config_path if args.config_path else get_claude_config_path() 84 | if claude_path is None: 85 | print( 86 | "Could not find Claude config path automatically. Please specify it using --config-path argument. The argument should be an absolute path of the claude_desktop_config.json file." 87 | ) 88 | sys.exit(1) 89 | 90 | claude_path.mkdir(parents=True, exist_ok=True) 91 | print("Writing config to", claude_path / "claude_desktop_config.json") 92 | with open(claude_path / "claude_desktop_config.json", "w") as f: 93 | json.dump(config, f, indent=2) 94 | -------------------------------------------------------------------------------- /tests/test_integration.py: -------------------------------------------------------------------------------- 1 | """Integration tests for WavespeedMCP. 2 | 3 | These tests demonstrate how to use the WavespeedMCP service in a real-world scenario. 4 | Note: These tests require a valid WaveSpeed API key to run. 5 | """ 6 | 7 | import os 8 | import asyncio 9 | import unittest 10 | import json 11 | from dotenv import load_dotenv 12 | from mcp import ClientSession 13 | from mcp.client.stdio import stdio_client 14 | from mcp import StdioServerParameters 15 | 16 | 17 | class TestWavespeedIntegration(unittest.TestCase): 18 | """Integration tests for WavespeedMCP.""" 19 | 20 | @classmethod 21 | def setUpClass(cls): 22 | """Set up the test environment.""" 23 | load_dotenv() 24 | cls.api_key = os.getenv("WAVESPEED_API_KEY") 25 | if not cls.api_key: 26 | raise unittest.SkipTest("WAVESPEED_API_KEY environment variable not set") 27 | 28 | async def _run_client(self, tool_name, params): 29 | """Run the client with the specified tool and parameters.""" 30 | server_params = StdioServerParameters( 31 | command="wavespeed-mcp", args=["--api-key", self.api_key] 32 | ) 33 | print(f"Running client with parameters: {server_params}") 34 | async with stdio_client(server_params) as (read_stream, write_stream): 35 | async with ClientSession(read_stream, write_stream) as client: 36 | await client.initialize() 37 | 38 | # Get available tools 39 | tools_result = await client.list_tools() 40 | tools = tools_result.tools 41 | 42 | # Verify tool is available 43 | tool_names = [tool.name for tool in tools] 44 | self.assertIn(tool_name, tool_names) 45 | 46 | # Call the tool 47 | result = await client.call_tool(tool_name, params) 48 | return result 49 | 50 | def test_generate_image(self): 51 | """Test generating an image.""" 52 | params = { 53 | "prompt": "A beautiful mountain landscape with a lake", 54 | "size": "512*512", # Smaller size for faster testing 55 | "num_images": 1, 56 | } 57 | 58 | print("Calling API with params:", params) 59 | result = asyncio.run(self._run_client("text_to_image", params)) 60 | print("Received API response: ", result) 61 | 62 | # Verify result 63 | print("Verifying result...") 64 | self.assertIsNotNone(result) 65 | self.assertTrue(len(result.content) > 0) 66 | 67 | # 验证返回的内容 68 | content = result.content[0] 69 | self.assertEqual(content.type, "text") 70 | self.assertTrue(content.text) 71 | 72 | # 解析 JSON 字符串 73 | data = json.loads(content.text) 74 | self.assertEqual(data["status"], "success") 75 | self.assertIn("urls", data) 76 | self.assertIsInstance(data["urls"], list) 77 | self.assertGreater(len(data["urls"]), 0) 78 | url = data["urls"][0] 79 | self.assertTrue(url.startswith("http")) 80 | self.assertIsNone(data["error"]) 81 | print(f"Generated image URL: {url}") 82 | 83 | def test_generate_video(self): 84 | """Test generating a video.""" 85 | # For testing, we'll use a sample image URL 86 | # In a real test, you would use a valid image URL 87 | params = { 88 | "image": "https://d2p7pge43lyniu.cloudfront.net/output/b4d0ccc2-a2f4-4495-b4a8-fcbb60d3ab82-u2_0af704ff-9f76-4b06-8d94-97b1975ff604.jpeg", 89 | "prompt": "A peaceful mountain scene with gentle wind", 90 | "duration": 5, # Short duration for faster testing 91 | } 92 | 93 | result = asyncio.run(self._run_client("generate_video", params)) 94 | 95 | # Verify result 96 | self.assertIsNotNone(result) 97 | 98 | # Check if we got a text response with a video URL 99 | # Verify result 100 | print("Verifying result...:", result) 101 | self.assertIsNotNone(result) 102 | self.assertTrue(len(result.content) > 0) 103 | 104 | 105 | if __name__ == "__main__": 106 | unittest.main() 107 | -------------------------------------------------------------------------------- /tests/test_wavespeed_mcp.py: -------------------------------------------------------------------------------- 1 | """Unit tests for WavespeedMCP.""" 2 | 3 | import os 4 | import unittest 5 | from unittest.mock import patch, MagicMock 6 | from pathlib import Path 7 | 8 | from wavespeed_mcp.utils import ( 9 | build_output_path, 10 | build_output_file, 11 | validate_loras 12 | ) 13 | from wavespeed_mcp.exceptions import WavespeedMcpError 14 | 15 | 16 | class TestWavespeedUtils(unittest.TestCase): 17 | """Test utility functions.""" 18 | 19 | def test_build_output_file(self): 20 | """Test build_output_file function.""" 21 | tool = "test_tool" 22 | description = "test description" 23 | output_path = Path("/tmp") 24 | extension = "png" 25 | 26 | filename = build_output_file(tool, description, output_path, extension) 27 | 28 | self.assertTrue(filename.startswith(f"{tool}_test_description_")) 29 | self.assertTrue(filename.endswith(f".{extension}")) 30 | 31 | @patch('wavespeed_mcp.utils.is_file_writeable') 32 | def test_build_output_path_default(self, mock_is_writeable): 33 | """Test build_output_path with default values.""" 34 | mock_is_writeable.return_value = True 35 | 36 | with patch('pathlib.Path.mkdir') as mock_mkdir: 37 | path = build_output_path() 38 | 39 | self.assertEqual(path, Path.home() / "Desktop") 40 | mock_mkdir.assert_called_once_with(parents=True, exist_ok=True) 41 | 42 | @patch('wavespeed_mcp.utils.is_file_writeable') 43 | def test_build_output_path_custom(self, mock_is_writeable): 44 | """Test build_output_path with custom directory.""" 45 | mock_is_writeable.return_value = True 46 | 47 | with patch('pathlib.Path.mkdir') as mock_mkdir: 48 | path = build_output_path("/custom/path") 49 | 50 | self.assertEqual(path, Path("/custom/path")) 51 | mock_mkdir.assert_called_once_with(parents=True, exist_ok=True) 52 | 53 | @patch('wavespeed_mcp.utils.is_file_writeable') 54 | def test_build_output_path_not_writeable(self, mock_is_writeable): 55 | """Test build_output_path with non-writeable directory.""" 56 | mock_is_writeable.return_value = False 57 | 58 | with self.assertRaises(WavespeedMcpError): 59 | build_output_path("/non/writeable/path") 60 | 61 | def test_validate_loras_valid(self): 62 | """Test validate_loras with valid loras.""" 63 | loras = [ 64 | {"path": "test/path", "scale": 1.0}, 65 | {"path": "another/path"} 66 | ] 67 | 68 | result = validate_loras(loras) 69 | 70 | self.assertEqual(len(result), 2) 71 | self.assertEqual(result[0]["path"], "test/path") 72 | self.assertEqual(result[0]["scale"], 1.0) 73 | self.assertEqual(result[1]["path"], "another/path") 74 | self.assertEqual(result[1]["scale"], 1.0) # Default scale added 75 | 76 | def test_validate_loras_invalid(self): 77 | """Test validate_loras with invalid loras.""" 78 | # Missing path 79 | with self.assertRaises(WavespeedMcpError): 80 | validate_loras([{"scale": 1.0}]) 81 | 82 | # Not a dict 83 | with self.assertRaises(WavespeedMcpError): 84 | validate_loras(["not a dict"]) 85 | 86 | def test_validate_loras_empty(self): 87 | """Test validate_loras with empty loras.""" 88 | self.assertEqual(validate_loras([]), []) 89 | self.assertEqual(validate_loras(None), []) 90 | 91 | 92 | class TestWavespeedClient(unittest.TestCase): 93 | """Test API client.""" 94 | 95 | @patch('wavespeed_mcp.client.requests.Session') 96 | def test_client_initialization(self, mock_session): 97 | """Test client initialization.""" 98 | from wavespeed_mcp.client import WavespeedAPIClient 99 | 100 | api_key = "test_key" 101 | api_host = "https://test.host" 102 | 103 | client = WavespeedAPIClient(api_key, api_host) 104 | 105 | self.assertEqual(client.api_key, api_key) 106 | self.assertEqual(client.api_host, api_host) 107 | 108 | # Check headers 109 | headers = mock_session.return_value.headers.update.call_args[0][0] 110 | self.assertEqual(headers["Authorization"], f"Bearer {api_key}") 111 | self.assertEqual(headers["Content-Type"], "application/json") 112 | 113 | 114 | if __name__ == "__main__": 115 | unittest.main() 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WavespeedMCP 2 | 3 | ## [English](README.md) | [中文文档](README.zh.md) 4 | 5 | WavespeedMCP is a Model Control Protocol (MCP) server implementation for WaveSpeed AI services. It provides a standardized interface for accessing WaveSpeed's image and video generation capabilities through the MCP protocol. 6 | 7 | ## Features 8 | 9 | - **Advanced Image Generation**: Create high-quality images from text prompts with support for image-to-image generation, inpainting, and LoRA models 10 | - **Dynamic Video Generation**: Transform static images into videos with customizable motion parameters 11 | - **Optimized Performance**: Enhanced API polling with intelligent retry logic and detailed progress tracking 12 | - **Flexible Resource Handling**: Support for URL, Base64, and local file output modes 13 | - **Comprehensive Error Handling**: Specialized exception hierarchy for precise error identification and recovery 14 | - **Robust Logging**: Detailed logging system for monitoring and debugging 15 | - **Multiple Configuration Options**: Support for environment variables, command-line arguments, and configuration files 16 | 17 | ## Installation 18 | 19 | ### Prerequisites 20 | 21 | - Python 3.11+ 22 | - WaveSpeed API key (obtain from [WaveSpeed AI](https://wavespeed.ai)) 23 | 24 | ### Setup 25 | 26 | Install directly from PyPI: 27 | 28 | ```bash 29 | pip install wavespeed-mcp 30 | ``` 31 | 32 | ### MCP Configuration 33 | 34 | To use WavespeedMCP with your IDE or application, add the following configuration: 35 | 36 | ```json 37 | { 38 | "mcpServers": { 39 | "WaveSpeed": { 40 | "command": "wavespeed-mcp", 41 | "env": { 42 | "WAVESPEED_API_KEY": "your-api-key-here", 43 | "WAVESPEED_LOG_FILE": "/tmp/wavespeed-mcp.log" 44 | } 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | ## Usage 51 | 52 | ### Running the Server 53 | 54 | Start the WavespeedMCP server: 55 | 56 | ```bash 57 | wavespeed-mcp --api-key your_api_key_here 58 | ``` 59 | 60 | ### Claude Desktop Integration 61 | 62 | WavespeedMCP can be integrated with Claude Desktop. To generate the necessary configuration file: 63 | 64 | ```bash 65 | python -m wavespeed_mcp --api-key your_api_key_here --config-path /path/to/claude/config 66 | ``` 67 | 68 | This command generates a `claude_desktop_config.json` file that configures Claude Desktop to use WavespeedMCP tools. After generating the configuration: 69 | 70 | 1. Start the WavespeedMCP server using the `wavespeed-mcp` command 71 | 2. Launch Claude Desktop, which will use the configured WavespeedMCP tools 72 | 73 | ## Configuration Options 74 | 75 | WavespeedMCP can be configured through: 76 | 77 | 1. **Environment Variables**: 78 | 79 | - `WAVESPEED_API_KEY`: Your WaveSpeed API key (required) 80 | - `WAVESPEED_API_HOST`: API host URL (default: https://api.wavespeed.ai) 81 | - `WAVESPEED_MCP_BASE_PATH`: Base path for saving generated files (default: ~/Desktop) 82 | - `WAVESPEED_API_RESOURCE_MODE`: Resource output mode - `url`, `local`, or `base64` (default: url) 83 | - `WAVESPEED_LOG_LEVEL`: Logging level - DEBUG, INFO, WARNING, ERROR (default: INFO) 84 | - `WAVESPEED_LOG_FILE`: Optional log file path (if not set, logs to console) 85 | - `WAVESPEED_API_TEXT_TO_IMAGE_ENDPOINT`: Custom endpoint for text-to-image generation (default: /wavespeed-ai/flux-dev) 86 | - `WAVESPEED_API_IMAGE_TO_IMAGE_ENDPOINT`: Custom endpoint for image-to-image generation (default: /wavespeed-ai/flux-kontext-pro) 87 | - `WAVESPEED_API_VIDEO_ENDPOINT`: Custom endpoint for video generation (default: /wavespeed-ai/wan-2.1/i2v-480p-lora) 88 | 89 | ### Timeouts 90 | 91 | WavespeedMCP supports two types of timeouts. Configure them via environment variables: 92 | 93 | - `WAVESPEED_REQUEST_TIMEOUT`: Per-HTTP request timeout in seconds (default: 300 = 5 minutes). 94 | This applies to individual HTTP calls made by the client, such as submitting a job or downloading outputs. 95 | 96 | - `WAVESPEED_WAIT_RESULT_TIMEOUT`: Total timeout for waiting/polling results in seconds (default: 600 = 10 minutes). 97 | This limits the overall time spent polling for an asynchronous job result. When exceeded, polling stops with a timeout error. 98 | 99 | Example: 100 | 101 | ```bash 102 | export WAVESPEED_REQUEST_TIMEOUT=300 # per HTTP request 103 | export WAVESPEED_WAIT_RESULT_TIMEOUT=900 # total wait for result (polling) 104 | ``` 105 | 106 | ### Logging Configuration 107 | 108 | By default, the MCP server logs to console. You can configure file logging by setting the `WAVESPEED_LOG_FILE` environment variable: 109 | 110 | ```bash 111 | # Log to /tmp directory 112 | export WAVESPEED_LOG_FILE=/tmp/wavespeed-mcp.log 113 | 114 | # Log to system log directory 115 | export WAVESPEED_LOG_FILE=/var/log/wavespeed-mcp.log 116 | 117 | # Log to user home directory 118 | export WAVESPEED_LOG_FILE=~/logs/wavespeed-mcp.log 119 | ``` 120 | 121 | The log file uses rotating file handler with: 122 | - Maximum file size: 10MB 123 | - Backup count: 5 files 124 | - Log format: `%(asctime)s - wavespeed-mcp - %(levelname)s - %(message)s` 125 | 126 | 2. **Command-line Arguments**: 127 | 128 | - `--api-key`: Your WaveSpeed API key 129 | - `--api-host`: API host URL 130 | - `--config`: Path to configuration file 131 | 132 | 3. **Configuration File** (JSON format): 133 | See `wavespeed_mcp_config_demo.json` for an example. 134 | 135 | ## Architecture 136 | 137 | WavespeedMCP follows a clean, modular architecture: 138 | 139 | - `server.py`: Core MCP server implementation with tool definitions 140 | - `client.py`: Optimized API client with intelligent polling 141 | - `utils.py`: Comprehensive utility functions for resource handling 142 | - `exceptions.py`: Specialized exception hierarchy for error handling 143 | - `const.py`: Constants and default configuration values 144 | 145 | ## Development 146 | 147 | ### Requirements 148 | 149 | - Python 3.11+ 150 | - Development dependencies: `pip install -e ".[dev]"` 151 | 152 | ### Testing 153 | 154 | Run the test suite: 155 | 156 | ```bash 157 | pytest 158 | ``` 159 | 160 | Or with coverage reporting: 161 | 162 | ```bash 163 | pytest --cov=wavespeed_mcp 164 | ``` 165 | 166 | ## License 167 | 168 | This project is licensed under the MIT License - see the LICENSE file for details. 169 | 170 | ## Support 171 | 172 | For support or feature requests, please contact the WaveSpeed AI team at support@wavespeed.ai. 173 | -------------------------------------------------------------------------------- /wavespeed_mcp/client.py: -------------------------------------------------------------------------------- 1 | """WaveSpeed API client base class.""" 2 | 3 | import time 4 | import os 5 | import requests 6 | import logging 7 | from typing import Dict, Any 8 | 9 | from wavespeed_mcp.exceptions import ( 10 | WavespeedAuthError, 11 | WavespeedRequestError, 12 | WavespeedTimeoutError, 13 | ) 14 | from wavespeed_mcp.const import ( 15 | API_PREDICTION_ENDPOINT, 16 | DEFAULT_WAIT_RESULT_TIMEOUT, 17 | ENV_WAVESPEED_WAIT_RESULT_TIMEOUT, 18 | ) 19 | 20 | logger = logging.getLogger("wavespeed-client") 21 | 22 | 23 | class WavespeedAPIClient: 24 | """Base client for making requests to WaveSpeed API.""" 25 | 26 | def __init__(self, api_key: str, api_host: str): 27 | """Initialize the API client. 28 | 29 | Args: 30 | api_key: The API key for authentication 31 | api_host: The API host URL 32 | """ 33 | self.api_key = api_key 34 | self.api_host = api_host 35 | self.session = requests.Session() 36 | self.session.headers.update( 37 | {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} 38 | ) 39 | 40 | def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]: 41 | """Make an HTTP request to the WaveSpeed API. 42 | 43 | Args: 44 | method: HTTP method (GET, POST, etc.) 45 | endpoint: API endpoint path 46 | **kwargs: Additional arguments to pass to requests 47 | 48 | Returns: 49 | API response data as dictionary 50 | 51 | Raises: 52 | WavespeedAuthError: If authentication fails 53 | WavespeedRequestError: If the request fails 54 | """ 55 | host = self.api_host.rstrip("/") 56 | path = endpoint if endpoint.startswith("/") else f"/{endpoint}" 57 | url = f"{host}{path}" 58 | 59 | logger.debug(f"Making {method} request to {url}") 60 | 61 | # Add timeout to prevent hanging 62 | if "timeout" not in kwargs: 63 | from wavespeed_mcp.const import ( 64 | DEFAULT_REQUEST_TIMEOUT, 65 | ENV_WAVESPEED_REQUEST_TIMEOUT, 66 | ) 67 | import os 68 | 69 | timeout = int( 70 | os.getenv(ENV_WAVESPEED_REQUEST_TIMEOUT, DEFAULT_REQUEST_TIMEOUT) 71 | ) 72 | kwargs["timeout"] = timeout 73 | 74 | response = None 75 | try: 76 | response = self.session.request(method, url, **kwargs) 77 | 78 | # Check for HTTP errors 79 | response.raise_for_status() 80 | 81 | data = response.json() 82 | 83 | # Check for API-specific errors 84 | if "error" in data: 85 | error_msg = data.get("error", "Unknown API error") 86 | raise WavespeedRequestError(f"API Error: {error_msg}") 87 | 88 | return data 89 | 90 | except requests.exceptions.Timeout: 91 | timeout = kwargs.get("timeout", 300) 92 | raise WavespeedTimeoutError( 93 | f"Request to {url} timed out after {timeout} seconds" 94 | ) 95 | except requests.exceptions.RequestException as e: 96 | if response is not None: 97 | if response.status_code == 401: 98 | raise WavespeedAuthError(f"Authentication failed: {str(e)}") 99 | if hasattr(response, "text") and response.text: 100 | raise WavespeedRequestError(f"Request failed: {response.text}") 101 | raise WavespeedRequestError(f"Request failed: {str(e)}") 102 | 103 | def get(self, endpoint: str, **kwargs) -> Dict[str, Any]: 104 | """Make a GET request.""" 105 | return self._make_request("GET", endpoint, **kwargs) 106 | 107 | def post(self, endpoint: str, **kwargs) -> Dict[str, Any]: 108 | """Make a POST request.""" 109 | return self._make_request("POST", endpoint, **kwargs) 110 | 111 | def poll_result( 112 | self, 113 | wavespeed_request_id: str, 114 | max_retries: int = -1, 115 | poll_interval: float = 0.5, 116 | request_id: str = None, 117 | total_timeout: float = None, 118 | ) -> Dict[str, Any]: 119 | """Poll for the result of an asynchronous API request. 120 | 121 | Args: 122 | wavespeed_request_id: The WaveSpeed API request ID to poll for 123 | max_retries: Maximum number of polling attempts. -1 for infinite retries. 124 | poll_interval: Time in seconds between polling attempts 125 | request_id: Optional MCP request ID for logging correlation 126 | total_timeout: Optional maximum total polling time in seconds. If not provided, 127 | will use ENV_WAVESPEED_REQUEST_TIMEOUT or DEFAULT_REQUEST_TIMEOUT. 128 | 129 | Returns: 130 | The final result of the API request 131 | 132 | Raises: 133 | WavespeedTimeoutError: If polling exceeds max_retries 134 | WavespeedRequestError: If the request fails 135 | """ 136 | result_url = f"{API_PREDICTION_ENDPOINT}/{wavespeed_request_id}/result" 137 | 138 | attempt = 0 139 | 140 | log_prefix = f"[{request_id}]" if request_id else "" 141 | logger.info( 142 | f"{log_prefix} Starting API polling for WaveSpeed ID: {wavespeed_request_id}" 143 | ) 144 | 145 | start_time = time.time() 146 | # Resolve total timeout using WAIT_RESULT env/default 147 | if total_timeout is None: 148 | try: 149 | total_timeout = float( 150 | os.getenv( 151 | ENV_WAVESPEED_WAIT_RESULT_TIMEOUT, DEFAULT_WAIT_RESULT_TIMEOUT 152 | ) 153 | ) 154 | except Exception: 155 | total_timeout = float(DEFAULT_WAIT_RESULT_TIMEOUT) 156 | 157 | while True: 158 | if max_retries != -1 and attempt >= max_retries: 159 | elapsed = time.time() - start_time 160 | logger.error( 161 | f"{log_prefix} API polling timed out after {max_retries} attempts ({elapsed:.1f}s)" 162 | ) 163 | raise WavespeedTimeoutError( 164 | f"Polling timed out after {max_retries} attempts" 165 | ) 166 | 167 | # Check total timeout window 168 | elapsed_total = time.time() - start_time 169 | if total_timeout is not None and total_timeout > 0 and elapsed_total >= total_timeout: 170 | logger.error( 171 | ( 172 | f"{log_prefix} API polling exceeded total timeout of " 173 | f"{total_timeout:.1f}s after {attempt+1} attempts" 174 | ) 175 | ) 176 | raise WavespeedTimeoutError( 177 | f"Polling timed out after {total_timeout:.1f} seconds" 178 | ) 179 | 180 | try: 181 | # Reduce log frequency - only log every 20 attempts (10 seconds) 182 | if attempt % 20 == 0 and attempt > 0: 183 | logger.debug( 184 | ( 185 | f"{log_prefix} Polling attempt {attempt+1}/" 186 | f"{max_retries if max_retries != -1 else '∞'} " 187 | f"({time.time() - start_time:.1f}s elapsed)" 188 | ) 189 | ) 190 | 191 | response = self.get(result_url) 192 | result = response.get("data", {}) 193 | status = result.get("status") 194 | 195 | # Only log status changes, not every poll 196 | if not hasattr(self, "_last_status") or self._last_status != status: 197 | logger.debug(f"{log_prefix} Status changed to: {status}") 198 | self._last_status = status 199 | 200 | if status == "completed": 201 | elapsed = time.time() - start_time 202 | logger.info( 203 | f"{log_prefix} API polling completed after {attempt+1} attempts ({elapsed:.1f}s)" 204 | ) 205 | return result 206 | elif status == "failed": 207 | elapsed = time.time() - start_time 208 | logger.error( 209 | f"{log_prefix} API polling failed after {attempt+1} attempts ({elapsed:.1f}s)" 210 | ) 211 | error = result.get("error", "unknown error") 212 | raise WavespeedRequestError(f"API request failed: {error}") 213 | 214 | # If still processing, wait and try again 215 | time.sleep(poll_interval) 216 | attempt += 1 217 | 218 | except WavespeedRequestError as e: 219 | # If it's a request error, re-raise it 220 | elapsed = time.time() - start_time 221 | logger.error(f"{log_prefix} Request failed: {str(e)}") 222 | raise 223 | except Exception as e: 224 | # For other exceptions, log and continue polling 225 | logger.warning(f"{log_prefix} Error during polling: {str(e)}") 226 | -------------------------------------------------------------------------------- /wavespeed_mcp/utils.py: -------------------------------------------------------------------------------- 1 | """Utility functions for WavespeedMCP.""" 2 | 3 | import os 4 | import logging 5 | from pathlib import Path 6 | from datetime import datetime 7 | from typing import Dict, List, Optional, Union 8 | from wavespeed_mcp.exceptions import WavespeedMcpError 9 | 10 | 11 | logger = logging.getLogger("wavespeed-utils") 12 | 13 | 14 | def is_file_writeable(path: Path) -> bool: 15 | """Check if a file or directory is writeable. 16 | 17 | Args: 18 | path: Path to check 19 | 20 | Returns: 21 | True if the path is writeable, False otherwise 22 | """ 23 | if path.exists(): 24 | return os.access(path, os.W_OK) 25 | parent_dir = path.parent 26 | return os.access(parent_dir, os.W_OK) 27 | 28 | 29 | def build_output_file( 30 | tool: str, description: str, output_path: Path, extension: str 31 | ) -> Path: 32 | """Build an output filename based on the tool and description. 33 | 34 | Args: 35 | tool: Name of the tool generating the file 36 | description: Brief description to include in the filename 37 | output_path: Directory to save the file in 38 | extension: File extension 39 | 40 | Returns: 41 | Path object for the output file 42 | """ 43 | # Use a short version of the description for the filename 44 | short_desc = description[:20].replace(" ", "_") 45 | 46 | # Create a filename with timestamp 47 | output_file_name = ( 48 | f"{tool}_{short_desc}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{extension}" 49 | ) 50 | # Return the full path (output_path / filename) 51 | return output_path / output_file_name 52 | 53 | 54 | def build_output_path( 55 | output_directory: Optional[str] = None, base_path: Optional[str] = None 56 | ) -> Path: 57 | """Build the output path for saving files. 58 | 59 | Args: 60 | output_directory: User-specified output directory 61 | base_path: Base path to use if output_directory is not absolute 62 | 63 | Returns: 64 | Path object for the output directory 65 | 66 | Raises: 67 | WavespeedMcpError: If the directory is not writeable 68 | """ 69 | # Set default base_path to desktop if not provided 70 | if base_path is None: 71 | base_path = str(Path.home() / "Desktop") 72 | 73 | # Ensure base_path is expanded properly 74 | expanded_base_path = os.path.expanduser(base_path) 75 | 76 | # Handle output path based on output_directory 77 | if output_directory is None: 78 | # If no output directory specified, use the expanded base path 79 | output_path = Path(expanded_base_path) 80 | elif not os.path.isabs(os.path.expanduser(output_directory)): 81 | # For relative paths, join with the expanded base path 82 | output_path = Path(expanded_base_path) / Path(output_directory) 83 | else: 84 | # For absolute paths, use the expanded output directory 85 | output_path = Path(os.path.expanduser(output_directory)) 86 | 87 | # Safety checks and directory creation 88 | if not is_file_writeable(output_path): 89 | raise WavespeedMcpError(f"Directory ({output_path}) is not writeable") 90 | 91 | output_path.mkdir(parents=True, exist_ok=True) 92 | return output_path 93 | 94 | 95 | def save_image_from_url(url: str, output_path: Path, filename: str) -> Path: 96 | """Download and save an image from a URL. 97 | 98 | Args: 99 | url: URL of the image to download 100 | output_path: Directory to save the image in 101 | filename: Filename to save the image as 102 | 103 | Returns: 104 | Path object for the saved image 105 | 106 | Raises: 107 | WavespeedMcpError: If the image cannot be downloaded or saved 108 | """ 109 | import requests 110 | 111 | try: 112 | response = requests.get(url, stream=True) 113 | response.raise_for_status() 114 | 115 | full_path = output_path / filename 116 | with open(full_path, "wb") as f: 117 | for chunk in response.iter_content(chunk_size=8192): 118 | f.write(chunk) 119 | 120 | return full_path 121 | 122 | except Exception as e: 123 | raise WavespeedMcpError(f"Failed to save image from URL: {str(e)}") 124 | 125 | 126 | def save_base64_image(base64_data: str, output_path: Path, filename: str) -> Path: 127 | """Save a base64 encoded image to a file. 128 | 129 | Args: 130 | base64_data: Base64 encoded image data 131 | output_path: Directory to save the image in 132 | filename: Filename to save the image as 133 | 134 | Returns: 135 | Path object for the saved image 136 | 137 | Raises: 138 | WavespeedMcpError: If the image cannot be saved 139 | """ 140 | import base64 141 | 142 | try: 143 | # Create the output directory if it doesn't exist 144 | output_path.mkdir(parents=True, exist_ok=True) 145 | 146 | # Decode the base64 data 147 | image_data = base64.b64decode(base64_data) 148 | 149 | # Save the image 150 | full_path = output_path / filename 151 | with open(full_path, "wb") as f: 152 | f.write(image_data) 153 | 154 | return full_path 155 | 156 | except Exception as e: 157 | raise WavespeedMcpError(f"Failed to save base64 image: {str(e)}") 158 | 159 | 160 | def get_image_as_base64(url: str, timeout: int = 30) -> tuple[str, str]: 161 | """Download an image from a URL and convert it to base64. 162 | 163 | Args: 164 | url: URL of the image to download 165 | timeout: Request timeout in seconds 166 | 167 | Returns: 168 | Tuple of (base64_string, mime_type) 169 | 170 | Raises: 171 | WavespeedMcpError: If the image cannot be downloaded or converted 172 | """ 173 | import requests 174 | import base64 175 | 176 | try: 177 | response = requests.get(url, stream=True, timeout=timeout) 178 | response.raise_for_status() 179 | 180 | # Get content type 181 | content_type = response.headers.get("content-type", "image/jpeg") 182 | if not content_type.startswith("image/"): 183 | raise WavespeedMcpError(f"Invalid content type: {content_type}") 184 | 185 | # Get image data 186 | image_data = response.content 187 | 188 | # Convert to base64 189 | base64_data = base64.b64encode(image_data).decode("utf-8") 190 | 191 | return base64_data, content_type 192 | 193 | except requests.Timeout: 194 | raise WavespeedMcpError( 195 | f"Timeout downloading image from URL (after {timeout}s)" 196 | ) 197 | except requests.RequestException as e: 198 | raise WavespeedMcpError(f"Failed to download image: {str(e)}") 199 | except Exception as e: 200 | raise WavespeedMcpError(f"Unexpected error processing image: {str(e)}") 201 | 202 | 203 | def process_image_input(image_input: str) -> str: 204 | """Process different types of image input and return a format suitable for API calls. 205 | 206 | Args: 207 | image_input: Can be a URL, base64 string, or local file path 208 | 209 | Returns: 210 | Processed image string (URL or base64 with data URI prefix) 211 | 212 | Raises: 213 | WavespeedMcpError: If the image cannot be processed 214 | """ 215 | import base64 216 | import re 217 | from pathlib import Path 218 | 219 | # Check if input is empty 220 | if not image_input: 221 | return "" 222 | 223 | # Check if input is already a URL 224 | if image_input.startswith(("http://", "https://", "ftp://")): 225 | return image_input 226 | 227 | # Check if input is already a base64 string with data URI prefix 228 | if image_input.startswith("data:image/"): 229 | return image_input 230 | 231 | # Check if input is a base64 string without prefix 232 | # Base64 strings are typically long and contain only certain characters 233 | if len(image_input) > 100 and re.match(r"^[A-Za-z0-9+/]+={0,2}$", image_input): 234 | # Add data URI prefix 235 | return f"data:image/jpeg;base64,{image_input}" 236 | 237 | # Check if input is a local file path 238 | path = Path(image_input) 239 | if path.exists() and path.is_file(): 240 | try: 241 | # Read file and convert to base64 242 | with open(path, "rb") as f: 243 | file_data = f.read() 244 | 245 | # Determine MIME type based on file extension 246 | mime_type = "image/jpeg" # Default 247 | if path.suffix.lower() in [".png"]: 248 | mime_type = "image/png" 249 | elif path.suffix.lower() in [".gif"]: 250 | mime_type = "image/gif" 251 | elif path.suffix.lower() in [".webp"]: 252 | mime_type = "image/webp" 253 | 254 | # Convert to base64 and add data URI prefix 255 | base64_data = base64.b64encode(file_data).decode("utf-8") 256 | return f"data:{mime_type};base64,{base64_data}" 257 | except Exception as e: 258 | raise WavespeedMcpError(f"Failed to process local image file: {str(e)}") 259 | 260 | # If we get here, the input format is unknown 261 | raise WavespeedMcpError(f"Unrecognized image input format: {image_input[:50]}...") 262 | 263 | 264 | def is_english_text(text: str) -> bool: 265 | """ 266 | Check if a text is primarily in English. 267 | 268 | Args: 269 | text: The text to check 270 | 271 | Returns: 272 | True if the text is primarily in English, False otherwise 273 | """ 274 | # Simple heuristic: Check if most characters are ASCII 275 | # This is a basic check and can be improved with more sophisticated language detection 276 | if not text: 277 | return True 278 | 279 | # Count ASCII characters (English text is mostly ASCII) 280 | ascii_count = sum(1 for char in text if ord(char) < 128) 281 | # If more than 80% of characters are ASCII, consider it English 282 | return ascii_count / len(text) > 0.8 283 | 284 | 285 | def validate_loras( 286 | loras: Optional[List[Dict[str, Union[str, float]]]] 287 | ) -> List[Dict[str, Union[str, float]]]: 288 | """Validate lora configuration. 289 | 290 | Args: 291 | loras: List of lora configurations 292 | 293 | Returns: 294 | Validated list of lora configurations 295 | 296 | Raises: 297 | WavespeedMcpError: If loras are invalid 298 | """ 299 | if not loras: 300 | return [] 301 | 302 | for lora in loras: 303 | if not isinstance(lora, dict): 304 | raise WavespeedMcpError(f"Invalid lora configuration: {lora}") 305 | 306 | if "path" not in lora: 307 | raise WavespeedMcpError(f"Missing 'path' in lora configuration: {lora}") 308 | 309 | if "scale" not in lora: 310 | lora["scale"] = 1.0 311 | 312 | return loras 313 | -------------------------------------------------------------------------------- /wavespeed_mcp/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | WaveSpeed MCP Server 3 | 4 | This server connects to WaveSpeed AI API endpoints which may involve costs. 5 | Any tool that makes an API call is clearly marked with a cost warning. 6 | 7 | Note: Always ensure you have proper API credentials before using these tools. 8 | """ 9 | 10 | import os 11 | import requests 12 | import time 13 | import json 14 | import logging 15 | import uuid 16 | from typing import Dict, List, Optional, Union 17 | from pydantic import BaseModel 18 | from dotenv import load_dotenv 19 | from mcp.server.fastmcp import FastMCP 20 | from mcp.types import TextContent 21 | 22 | from wavespeed_mcp.utils import ( 23 | build_output_path, 24 | build_output_file, 25 | validate_loras, 26 | get_image_as_base64, 27 | process_image_input, 28 | is_english_text, 29 | ) 30 | from wavespeed_mcp.const import ( 31 | DEFAULT_IMAGE_SIZE, 32 | DEFAULT_NUM_INFERENCE_STEPS, 33 | DEFAULT_GUIDANCE_SCALE, 34 | DEFAULT_NUM_IMAGES, 35 | DEFAULT_SEED, 36 | DEFAULT_IMAGE_LORA, 37 | ENV_WAVESPEED_API_KEY, 38 | ENV_WAVESPEED_API_HOST, 39 | ENV_WAVESPEED_MCP_BASE_PATH, 40 | ENV_RESOURCE_MODE, 41 | RESOURCE_MODE_URL, 42 | RESOURCE_MODE_BASE64, 43 | DEFAULT_LOG_LEVEL, 44 | ENV_FASTMCP_LOG_LEVEL, 45 | ENV_WAVESPEED_LOG_FILE, 46 | DEFAULT_REQUEST_TIMEOUT, 47 | ENV_WAVESPEED_REQUEST_TIMEOUT, 48 | DEFAULT_WAIT_RESULT_TIMEOUT, 49 | ENV_WAVESPEED_WAIT_RESULT_TIMEOUT, 50 | API_VERSION, 51 | API_BASE_PATH, 52 | API_IMAGE_ENDPOINT, 53 | API_VIDEO_ENDPOINT, 54 | API_IMAGE_TO_IMAGE_ENDPOINT, 55 | ENV_API_TEXT_TO_IMAGE_ENDPOINT, 56 | ENV_API_IMAGE_TO_IMAGE_ENDPOINT, 57 | ENV_API_VIDEO_ENDPOINT, 58 | ) 59 | from wavespeed_mcp.exceptions import ( 60 | WavespeedRequestError, 61 | WavespeedAuthError, 62 | WavespeedTimeoutError, 63 | ) 64 | from wavespeed_mcp.client import WavespeedAPIClient 65 | 66 | # Load environment variables 67 | load_dotenv() 68 | 69 | # Configure logging 70 | log_file = os.getenv(ENV_WAVESPEED_LOG_FILE) 71 | log_level = os.getenv(ENV_FASTMCP_LOG_LEVEL, DEFAULT_LOG_LEVEL) 72 | log_format = "%(asctime)s - wavespeed-mcp - %(levelname)s - %(message)s" 73 | 74 | if log_file: 75 | # Configure file logging 76 | import logging.handlers 77 | 78 | # Create log directory if it doesn't exist 79 | log_dir = os.path.dirname(log_file) 80 | if log_dir and not os.path.exists(log_dir): 81 | os.makedirs(log_dir, exist_ok=True) 82 | 83 | # Configure rotating file handler 84 | handler = logging.handlers.RotatingFileHandler( 85 | log_file, maxBytes=10 * 1024 * 1024, backupCount=5 # 10MB max, 5 backups 86 | ) 87 | handler.setFormatter(logging.Formatter(log_format)) 88 | 89 | # Configure root logger 90 | logging.basicConfig( 91 | level=log_level, 92 | handlers=[handler], 93 | format=log_format, 94 | ) 95 | else: 96 | # Default console logging 97 | logging.basicConfig( 98 | level=log_level, 99 | format=log_format, 100 | ) 101 | 102 | logger = logging.getLogger("wavespeed-mcp") 103 | 104 | # Get configuration from environment variables 105 | api_key = os.getenv(ENV_WAVESPEED_API_KEY) 106 | api_host = os.getenv(ENV_WAVESPEED_API_HOST, "https://api.wavespeed.ai") 107 | base_path = os.getenv(ENV_WAVESPEED_MCP_BASE_PATH) or "~/Desktop" 108 | resource_mode = os.getenv(ENV_RESOURCE_MODE, RESOURCE_MODE_URL) 109 | # Per-request HTTP timeout 110 | request_timeout = int(os.getenv(ENV_WAVESPEED_REQUEST_TIMEOUT, DEFAULT_REQUEST_TIMEOUT)) 111 | # Total wait timeout for polling results 112 | wait_result_timeout = int( 113 | os.getenv(ENV_WAVESPEED_WAIT_RESULT_TIMEOUT, DEFAULT_WAIT_RESULT_TIMEOUT) 114 | ) 115 | 116 | # Validate required environment variables 117 | if not api_key: 118 | raise ValueError(f"{ENV_WAVESPEED_API_KEY} environment variable is required") 119 | 120 | # Initialize MCP server and API client 121 | mcp = FastMCP( 122 | "WaveSpeed", log_level=os.getenv(ENV_FASTMCP_LOG_LEVEL, DEFAULT_LOG_LEVEL) 123 | ) 124 | api_client = WavespeedAPIClient(api_key, f"{api_host}{API_BASE_PATH}/{API_VERSION}") 125 | 126 | 127 | class FileInfo(BaseModel): 128 | """Information about a local file.""" 129 | 130 | path: str 131 | index: int 132 | 133 | 134 | class Base64Info(BaseModel): 135 | """Information about a base64 encoded resource.""" 136 | 137 | data: str 138 | mime_type: str 139 | index: int 140 | 141 | 142 | class WaveSpeedResult(BaseModel): 143 | """Unified model for WaveSpeed generation results.""" 144 | 145 | status: str = "success" 146 | urls: List[str] = [] 147 | base64: List[Base64Info] = [] 148 | local_files: List[FileInfo] = [] 149 | error: Optional[str] = None 150 | processing_time: float = 0.0 151 | model: Optional[str] = None 152 | 153 | def to_json(self) -> str: 154 | """Convert the result to a JSON string.""" 155 | return json.dumps(self.model_dump(), indent=2) 156 | 157 | 158 | def _process_wavespeed_request( 159 | api_endpoint: str, 160 | payload: dict, 161 | output_directory: Optional[str], 162 | prompt: str, 163 | resource_type: str = "image", # "image" or "video" 164 | operation_name: str = "Generation", 165 | request_id: str = None, 166 | ) -> TextContent: 167 | """Process a WaveSpeed API request and handle the response. 168 | 169 | This is a common function to handle API requests, polling for results, 170 | and processing the output based on the resource mode. 171 | 172 | Args: 173 | api_endpoint: The API endpoint to call 174 | payload: The request payload 175 | output_directory: Directory to save generated files 176 | prompt: The prompt used for generation 177 | resource_type: Type of resource being generated ("image" or "video") 178 | operation_name: Name of the operation for logging 179 | 180 | Returns: 181 | TextContent with the result JSON 182 | """ 183 | 184 | begin_time = time.time() 185 | 186 | # Log API request details 187 | logger.info(f"[{request_id}] Making {operation_name} API request to {api_endpoint}") 188 | logger.info(f"[{request_id}] Request payload: {json.dumps(payload, indent=2)}") 189 | 190 | try: 191 | # Make API request 192 | response_data = api_client.post(api_endpoint, json=payload) 193 | wavespeed_request_id = response_data.get("data", {}).get("id") 194 | 195 | if not wavespeed_request_id: 196 | logger.error( 197 | f"[{request_id}] Failed to get WaveSpeed request ID from response" 198 | ) 199 | error_result = WaveSpeedResult( 200 | status="error", 201 | error="Failed to get request ID from response. Please try again.", 202 | ) 203 | return TextContent(type="text", text=error_result.to_json()) 204 | 205 | logger.info( 206 | f"[{request_id}] {operation_name} request submitted with WaveSpeed ID: {wavespeed_request_id}" 207 | ) 208 | 209 | # Poll for results using server-level wait_result_timeout 210 | result = api_client.poll_result( 211 | wavespeed_request_id, 212 | request_id=request_id, 213 | total_timeout=wait_result_timeout, 214 | ) 215 | outputs = result.get("outputs", []) 216 | 217 | if not outputs: 218 | error_result = WaveSpeedResult( 219 | status="error", 220 | error=f"No {resource_type} outputs received. Please try again.", 221 | ) 222 | return TextContent(type="text", text=error_result.to_json()) 223 | 224 | end = time.time() 225 | processing_time = end - begin_time 226 | 227 | model = result.get("model", "") 228 | 229 | logger.info( 230 | f"[{request_id}] {operation_name} completed in {processing_time:.2f} seconds" 231 | ) 232 | 233 | # Prepare result 234 | result = WaveSpeedResult( 235 | urls=outputs, processing_time=processing_time, model=model 236 | ) 237 | 238 | # Handle different resource modes 239 | if resource_mode == RESOURCE_MODE_URL: 240 | # Only return URLs 241 | pass 242 | elif resource_mode == RESOURCE_MODE_BASE64: 243 | # Get base64 encoding 244 | if resource_type == "video": 245 | # For video, usually just one is returned 246 | video_url = outputs[0] 247 | try: 248 | response = requests.get(video_url, timeout=request_timeout) 249 | response.raise_for_status() 250 | 251 | # Convert to base64 252 | import base64 253 | 254 | base64_data = base64.b64encode(response.content).decode("utf-8") 255 | 256 | result.base64.append( 257 | Base64Info(data=base64_data, mime_type="video/mp4", index=0) 258 | ) 259 | 260 | logger.info(f"Successfully encoded {resource_type} to base64") 261 | except Exception as e: 262 | logger.error(f"Failed to encode {resource_type}: {str(e)}") 263 | else: 264 | # For images, handle multiple outputs 265 | for i, url in enumerate(outputs): 266 | try: 267 | # Get base64 encoding and MIME type 268 | base64_data, mime_type = get_image_as_base64(url) 269 | result.base64.append( 270 | Base64Info(data=base64_data, mime_type=mime_type, index=i) 271 | ) 272 | logger.info( 273 | f"Successfully encoded {resource_type} {i+1}/{len(outputs)} to base64" 274 | ) 275 | except Exception as e: 276 | logger.error( 277 | f"Failed to encode {resource_type} {i+1}: {str(e)}" 278 | ) 279 | else: 280 | # Save to local file 281 | output_path = build_output_path(output_directory, base_path) 282 | output_path.mkdir(parents=True, exist_ok=True) 283 | 284 | if resource_type == "video": 285 | # For video, usually just one is returned 286 | video_url = outputs[0] 287 | try: 288 | filename = build_output_file( 289 | resource_type, prompt, output_path, "mp4" 290 | ) 291 | 292 | response = requests.get( 293 | video_url, stream=True, timeout=request_timeout 294 | ) 295 | response.raise_for_status() 296 | 297 | with open(filename, "wb") as f: 298 | for chunk in response.iter_content(chunk_size=8192): 299 | f.write(chunk) 300 | 301 | result.local_files.append(FileInfo(path=str(filename), index=0)) 302 | logger.info(f"Successfully saved {resource_type} to {filename}") 303 | except Exception as e: 304 | logger.error(f"Failed to save {resource_type}: {str(e)}") 305 | else: 306 | # For images, handle multiple outputs 307 | for i, url in enumerate(outputs): 308 | try: 309 | output_file_name = build_output_file( 310 | resource_type, f"{i}_{prompt}", output_path, "jpeg" 311 | ) 312 | 313 | response = requests.get(url, timeout=request_timeout) 314 | response.raise_for_status() 315 | 316 | with open(output_file_name, "wb") as f: 317 | f.write(response.content) 318 | 319 | result.local_files.append( 320 | FileInfo(path=str(output_file_name), index=i) 321 | ) 322 | logger.info( 323 | f"Successfully saved {resource_type} {i+1}/{len(outputs)} to {output_file_name}" 324 | ) 325 | except Exception as e: 326 | logger.error(f"Failed to save {resource_type} {i+1}: {str(e)}") 327 | 328 | # Return unified JSON structure 329 | return TextContent(type="text", text=result.to_json()) 330 | 331 | except (WavespeedAuthError, WavespeedRequestError, WavespeedTimeoutError) as e: 332 | logger.error(f"[{request_id}] {operation_name} failed: {str(e)}") 333 | error_result = WaveSpeedResult( 334 | status="error", error=f"Failed to generate {resource_type}: {str(e)}" 335 | ) 336 | return TextContent(type="text", text=error_result.to_json()) 337 | except Exception as e: 338 | logger.exception( 339 | f"[{request_id}] Unexpected error during {operation_name.lower()}: {str(e)}" 340 | ) 341 | error_result = WaveSpeedResult( 342 | status="error", error=f"An unexpected error occurred: {str(e)}" 343 | ) 344 | return TextContent(type="text", text=error_result.to_json()) 345 | 346 | 347 | def get_models(model): 348 | if model == "" or model is None: 349 | model = os.getenv(ENV_API_TEXT_TO_IMAGE_ENDPOINT, API_IMAGE_ENDPOINT) 350 | 351 | if not model.startswith("/"): 352 | model = "/" + model 353 | 354 | return model 355 | 356 | 357 | def get_image_models(model): 358 | if model == "" or model is None: 359 | model = os.getenv(ENV_API_IMAGE_TO_IMAGE_ENDPOINT, API_IMAGE_TO_IMAGE_ENDPOINT) 360 | 361 | if not model.startswith("/"): 362 | model = "/" + model 363 | 364 | return model 365 | 366 | 367 | def get_video_models(model): 368 | if model == "" or model is None: 369 | model = os.getenv(ENV_API_VIDEO_ENDPOINT, API_VIDEO_ENDPOINT) 370 | 371 | if not model.startswith("/"): 372 | model = "/" + model 373 | 374 | return model 375 | 376 | 377 | @mcp.tool( 378 | description="""Generate an image from text prompt using WaveSpeed AI. 379 | 380 | Args: 381 | prompt (str): Required. Text description of the image to generate. MUST BE IN ENGLISH. Non-English prompts will be rejected or result in poor quality outputs. 382 | model (str, optional): Model to use for image generation. 383 | loras (list, optional): List of LoRA models to use, each with a path and scale. Format: [{"path": "model_path", "scale": weight_value}]. Default model used if not provided. 384 | size (str, optional): Size of the output image in format "width*height", e.g., "512*512". Default: 1024*1024. 385 | num_inference_steps (int, optional): Number of denoising steps. Higher values improve quality but increase generation time. Default: 30. 386 | guidance_scale (float, optional): Guidance scale for text adherence. Controls how closely the image matches the text description. Default: 7.5. 387 | num_images (int, optional): Number of images to generate. Default: 1. 388 | seed (int, optional): Random seed for reproducible results. Set to -1 for random. Default: -1. 389 | enable_safety_checker (bool, optional): Whether to enable safety filtering. Default: True. 390 | output_directory (str, optional): Directory to save the generated images. Uses a temporary directory if not provided. 391 | request_id (str, optional): Request correlation ID for tracing the entire request chain. Strongly recommended to provide a unique ID (e.g., UUID) to correlate logs across the request lifecycle. 392 | 393 | Returns: 394 | WaveSpeedResult object with the result of the image generation, containing: 395 | - status: "success" or "error" 396 | - urls: List of image URLs if successful 397 | - base64: List of base64 encoded images if resource_mode is set to base64 398 | - local_files: List of local file paths if resource_mode is set to local 399 | - error: Error message if status is "error" 400 | - processing_time: Time taken to generate the image(s) 401 | 402 | Examples: 403 | Basic usage: text_to_image(prompt="A golden retriever running on grass") 404 | Advanced usage: text_to_image( 405 | prompt="A golden retriever running on grass", 406 | size="1024*1024", 407 | num_inference_steps=50, 408 | seed=42 409 | ) 410 | 411 | Note: 412 | For optimal results, always provide prompts in English, regardless of your interface language. 413 | Non-English prompts may result in lower quality or unexpected images. 414 | """ 415 | ) 416 | def text_to_image( 417 | prompt: str, 418 | model: Optional[str] = None, 419 | loras: Optional[List[Dict[str, Union[str, float]]]] = None, 420 | size: str = DEFAULT_IMAGE_SIZE, 421 | num_inference_steps: int = DEFAULT_NUM_INFERENCE_STEPS, 422 | guidance_scale: float = DEFAULT_GUIDANCE_SCALE, 423 | num_images: int = DEFAULT_NUM_IMAGES, 424 | seed: int = DEFAULT_SEED, 425 | enable_safety_checker: bool = True, 426 | output_directory: str = None, 427 | request_id: str = None, 428 | ): 429 | """Generate an image from text prompt using WaveSpeed AI.""" 430 | 431 | # Generate unique request ID for tracking 432 | if not request_id: 433 | request_id = str(uuid.uuid4())[:8] 434 | logger.info( 435 | f"[{request_id}] MCP text_to_image request - prompt: '{prompt[:100]}{'...' if len(prompt) > 100 else ''}', model: {model}, size: {size}, num_images: {num_images}" 436 | ) 437 | 438 | if not prompt: 439 | error_result = WaveSpeedResult( 440 | status="error", 441 | error="Prompt is required for image generation. Please provide an English prompt for optimal results.", 442 | ) 443 | return TextContent(type="text", text=error_result.to_json()) 444 | 445 | # Check if prompt is in English 446 | if not is_english_text(prompt): 447 | error_result = WaveSpeedResult( 448 | status="error", 449 | error="Prompt must be in English. Please provide an English prompt for optimal results.", 450 | ) 451 | return TextContent(type="text", text=error_result.to_json()) 452 | 453 | # Validate and set default loras if not provided 454 | # if not loras: 455 | # loras = [DEFAULT_IMAGE_LORA] 456 | # else: 457 | if loras: 458 | loras = validate_loras(loras) 459 | else: 460 | loras = [] 461 | 462 | # Prepare API payload 463 | payload = { 464 | "prompt": prompt, 465 | "loras": loras, 466 | "size": size, 467 | "num_inference_steps": num_inference_steps, 468 | "guidance_scale": guidance_scale, 469 | "num_images": num_images, 470 | "max_images": num_images, 471 | "seed": seed, 472 | "enable_base64_output": False, # 使用URL,后续自己转换为base64 473 | "enable_safety_checker": enable_safety_checker, 474 | } 475 | 476 | return _process_wavespeed_request( 477 | api_endpoint=get_models(model), 478 | payload=payload, 479 | output_directory=output_directory, 480 | prompt=prompt, 481 | resource_type="image", 482 | operation_name="Image generation", 483 | request_id=request_id, 484 | ) 485 | 486 | 487 | @mcp.tool( 488 | description="""Generate an image from an existing image using WaveSpeed AI. 489 | 490 | Args: 491 | image (str): Required. URL, base64 string, or local file path of the input image to modify. 492 | images (List[str]): Required. List of URLs to images to modify. 493 | prompt (str): Required. Text description of the desired modifications. MUST BE IN ENGLISH. Non-English prompts will be rejected or result in poor quality outputs. 494 | model (str, optional): Model to use for image generation. 495 | guidance_scale (float, optional): Guidance scale for text adherence. Controls how closely the output follows the prompt. Range: [1.0-10.0]. Default: 3.5. 496 | enable_safety_checker (bool, optional): Whether to enable safety filtering. Default: True. 497 | output_directory (str, optional): Directory to save the generated images. Uses a temporary directory if not provided. 498 | request_id (str, optional): Request correlation ID for tracing the entire request chain. Strongly recommended to provide a unique ID (e.g., UUID) to correlate logs across the request lifecycle. 499 | 500 | Returns: 501 | WaveSpeedResult object with the result of the image generation, containing: 502 | - status: "success" or "error" 503 | - urls: List of image URLs if successful 504 | - base64: List of base64 encoded images if resource_mode is set to base64 505 | - local_files: List of local file paths if resource_mode is set to local 506 | - error: Error message if status is "error" 507 | - processing_time: Time taken to generate the image(s) 508 | 509 | Examples: 510 | Single image: image_to_image(image="https://example.com/image.jpg", images=[], prompt="Make it look like winter") 511 | Multiple images: image_to_image(image="", images=["https://example.com/img1.jpg", "https://example.com/img2.jpg"], prompt="Convert to oil painting style") 512 | Both parameters: image_to_image(image="https://example.com/main.jpg", images=["https://example.com/ref1.jpg"], prompt="Apply style transfer") 513 | 514 | Note: 515 | For optimal results, always provide prompts in English, regardless of your interface language. 516 | Non-English prompts may result in lower quality or unexpected images. 517 | """ 518 | ) 519 | def image_to_image( 520 | image: str, 521 | images: List[str], 522 | prompt: str, 523 | model: Optional[str] = None, 524 | guidance_scale: float = 3.5, 525 | enable_safety_checker: bool = True, 526 | output_directory: str = None, 527 | request_id: str = None, 528 | ): 529 | """Generate an image from an existing image using WaveSpeed AI.""" 530 | 531 | # Generate unique request ID for tracking 532 | if not request_id: 533 | request_id = str(uuid.uuid4())[:8] 534 | logger.info( 535 | f"[{request_id}] MCP image_to_image request - prompt: '{prompt[:100]}{'...' if len(prompt) > 100 else ''}', model: {model}, image: {image}, images_count: {len(images)}" 536 | ) 537 | 538 | if not image and not images: 539 | error_result = WaveSpeedResult( 540 | status="error", 541 | error="Input image(s) required for image-to-image generation. Provide either 'image' or 'images' parameter.", 542 | ) 543 | return TextContent(type="text", text=error_result.to_json()) 544 | 545 | if not prompt: 546 | error_result = WaveSpeedResult( 547 | status="error", 548 | error="Prompt is required for image-to-image generation. Please provide an English prompt for optimal results.", 549 | ) 550 | return TextContent(type="text", text=error_result.to_json()) 551 | 552 | # Check if prompt is in English 553 | if not is_english_text(prompt): 554 | error_result = WaveSpeedResult( 555 | status="error", 556 | error="Prompt must be in English. Please provide an English prompt for optimal results.", 557 | ) 558 | return TextContent(type="text", text=error_result.to_json()) 559 | 560 | # handle image input(s) 561 | payload = { 562 | "prompt": prompt, 563 | "guidance_scale": guidance_scale, 564 | "enable_safety_checker": enable_safety_checker, 565 | } 566 | 567 | try: 568 | # Process single image if provided 569 | if image: 570 | processed_image = process_image_input(image) 571 | payload["image"] = processed_image 572 | logger.info("Successfully processed single input image") 573 | 574 | # Process multiple images if provided 575 | if images: 576 | processed_images = [] 577 | for img in images: 578 | processed_img = process_image_input(img) 579 | processed_images.append(processed_img) 580 | payload["images"] = processed_images 581 | logger.info(f"Successfully processed {len(processed_images)} input images") 582 | 583 | if not image: 584 | image = images[0] 585 | payload["image"] = image 586 | 587 | if not images: 588 | images = [image] 589 | payload["images"] = images 590 | 591 | except Exception as e: 592 | logger.error(f"Failed to process input image(s): {str(e)}") 593 | error_result = WaveSpeedResult( 594 | status="error", error=f"Failed to process input image(s): {str(e)}" 595 | ) 596 | return TextContent(type="text", text=error_result.to_json()) 597 | 598 | return _process_wavespeed_request( 599 | api_endpoint=get_image_models(model), 600 | payload=payload, 601 | output_directory=output_directory, 602 | prompt=prompt, 603 | resource_type="image", 604 | operation_name="Image-to-image generation", 605 | request_id=request_id, 606 | ) 607 | 608 | 609 | @mcp.tool( 610 | description="""Generate a video using WaveSpeed AI. 611 | 612 | Args: 613 | image (str): Required. URL, base64 string, or local file path of the input image to animate. 614 | prompt (str): Required. Text description of the video to generate. MUST BE IN ENGLISH. Non-English prompts will be rejected or result in poor quality outputs. 615 | model (str, optional): Model to use for video generation. 616 | negative_prompt (str, optional): Text description of what to avoid in the video. Default: "". 617 | loras (list, optional): List of LoRA models to use, each with a path and scale. Format: [{"path": "model_path", "scale": weight_value}]. Default: []. 618 | size (str, optional): Size of the output video in format "width*height". Default: "832*480". 619 | num_inference_steps (int, optional): Number of denoising steps. Higher values improve quality but increase generation time. Default: 30. 620 | duration (int, optional): Duration of the video in seconds. Must be either 5 or 10. Default: 5. 621 | guidance_scale (float, optional): Guidance scale for text adherence. Controls how closely the video matches the text description. Default: 5. 622 | flow_shift (int, optional): Shift of the flow in the video. Affects motion intensity. Default: 3. 623 | seed (int, optional): Random seed for reproducible results. Set to -1 for random. Default: -1. 624 | enable_safety_checker (bool, optional): Whether to enable safety filtering. Default: True. 625 | output_directory (str, optional): Directory to save the generated video. Uses a temporary directory if not provided. 626 | request_id (str, optional): Request correlation ID for tracing the entire request chain. Strongly recommended to provide a unique ID (e.g., UUID) to correlate logs across the request lifecycle. 627 | 628 | Returns: 629 | WaveSpeedResult object with the result of the video generation, containing: 630 | - status: "success" or "error" 631 | - urls: List of video URLs if successful 632 | - base64: List of base64 encoded videos if resource_mode is set to base64 633 | - local_files: List of local file paths if resource_mode is set to local 634 | - error: Error message if status is "error" 635 | - processing_time: Time taken to generate the video(s) 636 | 637 | Examples: 638 | Basic usage: generate_video(image="https://example.com/image.jpg", prompt="The dog running through a forest") 639 | Advanced usage: generate_video( 640 | image="/path/to/local/image.jpg", 641 | prompt="The dog running through a forest", 642 | duration=10, 643 | negative_prompt="blurry, low quality" 644 | ) 645 | 646 | Note: 647 | IMPORTANT: Prompts MUST be in English. The system only processes English prompts properly. 648 | Non-English prompts will be rejected or produce low-quality results. If user input is not in English, 649 | you MUST translate it to English before passing to this tool. 650 | """ 651 | ) 652 | def generate_video( 653 | image: str, 654 | prompt: str, 655 | model: Optional[str] = None, 656 | negative_prompt: str = "", 657 | loras: Optional[List[Dict[str, Union[str, float]]]] = None, 658 | size: str = "832*480", 659 | num_inference_steps: int = 30, 660 | duration: int = 5, 661 | guidance_scale: float = 5, 662 | flow_shift: int = 3, 663 | seed: int = -1, 664 | enable_safety_checker: bool = True, 665 | output_directory: str = None, 666 | request_id: str = None, 667 | ): 668 | """Generate a video using WaveSpeed AI.""" 669 | 670 | # Generate unique request ID for tracking 671 | if not request_id: 672 | request_id = str(uuid.uuid4())[:8] 673 | logger.info( 674 | f"[{request_id}] MCP generate_video request - prompt: '{prompt[:100]}{'...' if len(prompt) > 100 else ''}', model: {model}, image: {image}, duration: {duration}s" 675 | ) 676 | 677 | if not image: 678 | # raise WavespeedRequestError("Input image is required for video generation") 679 | error_result = WaveSpeedResult( 680 | status="error", 681 | error="Input image is required for video generation. Can use generate_image tool to generate an image first.", 682 | ) 683 | return TextContent(type="text", text=error_result.to_json()) 684 | 685 | if not prompt: 686 | # raise WavespeedRequestError("Prompt is required for video generation") 687 | error_result = WaveSpeedResult( 688 | status="error", 689 | error="Prompt is required for video generation. Please provide an English prompt for optimal results.", 690 | ) 691 | return TextContent(type="text", text=error_result.to_json()) 692 | 693 | # Check if prompt is in English 694 | if not is_english_text(prompt): 695 | error_result = WaveSpeedResult( 696 | status="error", 697 | error="Prompt must be in English. Please provide an English prompt for optimal results.", 698 | ) 699 | return TextContent(type="text", text=error_result.to_json()) 700 | 701 | # Validate and set default loras if not provided 702 | if not loras: 703 | loras = [] 704 | else: 705 | loras = validate_loras(loras) 706 | 707 | if duration not in [5, 10]: 708 | error_result = WaveSpeedResult( 709 | status="error", 710 | error="Duration must be 5 or 10 seconds. Please set it to 5 or 10.", 711 | ) 712 | return TextContent(type="text", text=error_result.to_json()) 713 | 714 | # handle image input 715 | try: 716 | processed_image = process_image_input(image) 717 | logger.info("Successfully processed input image") 718 | except Exception as e: 719 | logger.error(f"Failed to process input image: {str(e)}") 720 | error_result = WaveSpeedResult( 721 | status="error", error=f"Failed to process input image: {str(e)}" 722 | ) 723 | return TextContent(type="text", text=error_result.to_json()) 724 | 725 | # Prepare API payload 726 | payload = { 727 | "image": processed_image, 728 | "prompt": prompt, 729 | "negative_prompt": negative_prompt, 730 | "loras": loras, 731 | "size": size, 732 | "num_inference_steps": num_inference_steps, 733 | "duration": duration, 734 | "guidance_scale": guidance_scale, 735 | "flow_shift": flow_shift, 736 | "seed": seed, 737 | "enable_safety_checker": enable_safety_checker, 738 | } 739 | 740 | return _process_wavespeed_request( 741 | api_endpoint=get_video_models(model), 742 | payload=payload, 743 | output_directory=output_directory, 744 | prompt=prompt, 745 | resource_type="video", 746 | operation_name="Video generation", 747 | request_id=request_id, 748 | ) 749 | 750 | 751 | def main(): 752 | print("Starting WaveSpeed MCP server") 753 | """Run the WaveSpeed MCP server""" 754 | mcp.run() 755 | 756 | 757 | if __name__ == "__main__": 758 | main() 759 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 2 3 | requires-python = ">=3.11" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.9.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "idna" }, 20 | { name = "sniffio" }, 21 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 22 | ] 23 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, 26 | ] 27 | 28 | [[package]] 29 | name = "backports-tarfile" 30 | version = "1.2.0" 31 | source = { registry = "https://pypi.org/simple" } 32 | sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, 35 | ] 36 | 37 | [[package]] 38 | name = "build" 39 | version = "1.2.2.post1" 40 | source = { registry = "https://pypi.org/simple" } 41 | dependencies = [ 42 | { name = "colorama", marker = "os_name == 'nt'" }, 43 | { name = "packaging" }, 44 | { name = "pyproject-hooks" }, 45 | ] 46 | sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701, upload-time = "2024-10-06T17:22:25.251Z" } 47 | wheels = [ 48 | { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950, upload-time = "2024-10-06T17:22:23.299Z" }, 49 | ] 50 | 51 | [[package]] 52 | name = "certifi" 53 | version = "2025.4.26" 54 | source = { registry = "https://pypi.org/simple" } 55 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } 56 | wheels = [ 57 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, 58 | ] 59 | 60 | [[package]] 61 | name = "cffi" 62 | version = "1.17.1" 63 | source = { registry = "https://pypi.org/simple" } 64 | dependencies = [ 65 | { name = "pycparser" }, 66 | ] 67 | sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } 68 | wheels = [ 69 | { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, 70 | { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, 71 | { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, 72 | { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, 73 | { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, 74 | { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, 75 | { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, 76 | { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, 77 | { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, 78 | { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, 79 | { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, 80 | { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, 81 | { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, 82 | { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, 83 | { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, 84 | { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, 85 | { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, 86 | { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, 87 | { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, 88 | { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, 89 | { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, 90 | { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, 91 | ] 92 | 93 | [[package]] 94 | name = "cfgv" 95 | version = "3.4.0" 96 | source = { registry = "https://pypi.org/simple" } 97 | sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } 98 | wheels = [ 99 | { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, 100 | ] 101 | 102 | [[package]] 103 | name = "charset-normalizer" 104 | version = "3.4.2" 105 | source = { registry = "https://pypi.org/simple" } 106 | sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } 107 | wheels = [ 108 | { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, 109 | { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, 110 | { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, 111 | { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, 112 | { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, 113 | { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, 114 | { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, 115 | { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, 116 | { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, 117 | { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, 118 | { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, 119 | { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, 120 | { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, 121 | { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, 122 | { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, 123 | { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, 124 | { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, 125 | { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, 126 | { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, 127 | { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, 128 | { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, 129 | { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, 130 | { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, 131 | { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, 132 | { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, 133 | { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, 134 | { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, 135 | { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, 136 | { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, 137 | { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, 138 | { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, 139 | { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, 140 | { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, 141 | { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, 142 | { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, 143 | { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, 144 | { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, 145 | { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, 146 | { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, 147 | { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, 148 | ] 149 | 150 | [[package]] 151 | name = "click" 152 | version = "8.2.1" 153 | source = { registry = "https://pypi.org/simple" } 154 | dependencies = [ 155 | { name = "colorama", marker = "sys_platform == 'win32'" }, 156 | ] 157 | sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } 158 | wheels = [ 159 | { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, 160 | ] 161 | 162 | [[package]] 163 | name = "colorama" 164 | version = "0.4.6" 165 | source = { registry = "https://pypi.org/simple" } 166 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 167 | wheels = [ 168 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 169 | ] 170 | 171 | [[package]] 172 | name = "coverage" 173 | version = "7.8.2" 174 | source = { registry = "https://pypi.org/simple" } 175 | sdist = { url = "https://files.pythonhosted.org/packages/ba/07/998afa4a0ecdf9b1981ae05415dad2d4e7716e1b1f00abbd91691ac09ac9/coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27", size = 812759, upload-time = "2025-05-23T11:39:57.856Z" } 176 | wheels = [ 177 | { url = "https://files.pythonhosted.org/packages/6a/4d/1ff618ee9f134d0de5cc1661582c21a65e06823f41caf801aadf18811a8e/coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9", size = 211692, upload-time = "2025-05-23T11:38:08.485Z" }, 178 | { url = "https://files.pythonhosted.org/packages/96/fa/c3c1b476de96f2bc7a8ca01a9f1fcb51c01c6b60a9d2c3e66194b2bdb4af/coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879", size = 212115, upload-time = "2025-05-23T11:38:09.989Z" }, 179 | { url = "https://files.pythonhosted.org/packages/f7/c2/5414c5a1b286c0f3881ae5adb49be1854ac5b7e99011501f81c8c1453065/coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a", size = 244740, upload-time = "2025-05-23T11:38:11.947Z" }, 180 | { url = "https://files.pythonhosted.org/packages/cd/46/1ae01912dfb06a642ef3dd9cf38ed4996fda8fe884dab8952da616f81a2b/coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5", size = 242429, upload-time = "2025-05-23T11:38:13.955Z" }, 181 | { url = "https://files.pythonhosted.org/packages/06/58/38c676aec594bfe2a87c7683942e5a30224791d8df99bcc8439fde140377/coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11", size = 244218, upload-time = "2025-05-23T11:38:15.631Z" }, 182 | { url = "https://files.pythonhosted.org/packages/80/0c/95b1023e881ce45006d9abc250f76c6cdab7134a1c182d9713878dfefcb2/coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a", size = 243865, upload-time = "2025-05-23T11:38:17.622Z" }, 183 | { url = "https://files.pythonhosted.org/packages/57/37/0ae95989285a39e0839c959fe854a3ae46c06610439350d1ab860bf020ac/coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb", size = 242038, upload-time = "2025-05-23T11:38:19.966Z" }, 184 | { url = "https://files.pythonhosted.org/packages/4d/82/40e55f7c0eb5e97cc62cbd9d0746fd24e8caf57be5a408b87529416e0c70/coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54", size = 242567, upload-time = "2025-05-23T11:38:21.912Z" }, 185 | { url = "https://files.pythonhosted.org/packages/f9/35/66a51adc273433a253989f0d9cc7aa6bcdb4855382cf0858200afe578861/coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a", size = 214194, upload-time = "2025-05-23T11:38:23.571Z" }, 186 | { url = "https://files.pythonhosted.org/packages/f6/8f/a543121f9f5f150eae092b08428cb4e6b6d2d134152c3357b77659d2a605/coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975", size = 215109, upload-time = "2025-05-23T11:38:25.137Z" }, 187 | { url = "https://files.pythonhosted.org/packages/77/65/6cc84b68d4f35186463cd7ab1da1169e9abb59870c0f6a57ea6aba95f861/coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53", size = 213521, upload-time = "2025-05-23T11:38:27.123Z" }, 188 | { url = "https://files.pythonhosted.org/packages/8d/2a/1da1ada2e3044fcd4a3254fb3576e160b8fe5b36d705c8a31f793423f763/coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c", size = 211876, upload-time = "2025-05-23T11:38:29.01Z" }, 189 | { url = "https://files.pythonhosted.org/packages/70/e9/3d715ffd5b6b17a8be80cd14a8917a002530a99943cc1939ad5bb2aa74b9/coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1", size = 212130, upload-time = "2025-05-23T11:38:30.675Z" }, 190 | { url = "https://files.pythonhosted.org/packages/a0/02/fdce62bb3c21649abfd91fbdcf041fb99be0d728ff00f3f9d54d97ed683e/coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279", size = 246176, upload-time = "2025-05-23T11:38:32.395Z" }, 191 | { url = "https://files.pythonhosted.org/packages/a7/52/decbbed61e03b6ffe85cd0fea360a5e04a5a98a7423f292aae62423b8557/coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99", size = 243068, upload-time = "2025-05-23T11:38:33.989Z" }, 192 | { url = "https://files.pythonhosted.org/packages/38/6c/d0e9c0cce18faef79a52778219a3c6ee8e336437da8eddd4ab3dbd8fadff/coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20", size = 245328, upload-time = "2025-05-23T11:38:35.568Z" }, 193 | { url = "https://files.pythonhosted.org/packages/f0/70/f703b553a2f6b6c70568c7e398ed0789d47f953d67fbba36a327714a7bca/coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2", size = 245099, upload-time = "2025-05-23T11:38:37.627Z" }, 194 | { url = "https://files.pythonhosted.org/packages/ec/fb/4cbb370dedae78460c3aacbdad9d249e853f3bc4ce5ff0e02b1983d03044/coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57", size = 243314, upload-time = "2025-05-23T11:38:39.238Z" }, 195 | { url = "https://files.pythonhosted.org/packages/39/9f/1afbb2cb9c8699b8bc38afdce00a3b4644904e6a38c7bf9005386c9305ec/coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f", size = 244489, upload-time = "2025-05-23T11:38:40.845Z" }, 196 | { url = "https://files.pythonhosted.org/packages/79/fa/f3e7ec7d220bff14aba7a4786ae47043770cbdceeea1803083059c878837/coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8", size = 214366, upload-time = "2025-05-23T11:38:43.551Z" }, 197 | { url = "https://files.pythonhosted.org/packages/54/aa/9cbeade19b7e8e853e7ffc261df885d66bf3a782c71cba06c17df271f9e6/coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223", size = 215165, upload-time = "2025-05-23T11:38:45.148Z" }, 198 | { url = "https://files.pythonhosted.org/packages/c4/73/e2528bf1237d2448f882bbebaec5c3500ef07301816c5c63464b9da4d88a/coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f", size = 213548, upload-time = "2025-05-23T11:38:46.74Z" }, 199 | { url = "https://files.pythonhosted.org/packages/1a/93/eb6400a745ad3b265bac36e8077fdffcf0268bdbbb6c02b7220b624c9b31/coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca", size = 211898, upload-time = "2025-05-23T11:38:49.066Z" }, 200 | { url = "https://files.pythonhosted.org/packages/1b/7c/bdbf113f92683024406a1cd226a199e4200a2001fc85d6a6e7e299e60253/coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d", size = 212171, upload-time = "2025-05-23T11:38:51.207Z" }, 201 | { url = "https://files.pythonhosted.org/packages/91/22/594513f9541a6b88eb0dba4d5da7d71596dadef6b17a12dc2c0e859818a9/coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85", size = 245564, upload-time = "2025-05-23T11:38:52.857Z" }, 202 | { url = "https://files.pythonhosted.org/packages/1f/f4/2860fd6abeebd9f2efcfe0fd376226938f22afc80c1943f363cd3c28421f/coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257", size = 242719, upload-time = "2025-05-23T11:38:54.529Z" }, 203 | { url = "https://files.pythonhosted.org/packages/89/60/f5f50f61b6332451520e6cdc2401700c48310c64bc2dd34027a47d6ab4ca/coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108", size = 244634, upload-time = "2025-05-23T11:38:57.326Z" }, 204 | { url = "https://files.pythonhosted.org/packages/3b/70/7f4e919039ab7d944276c446b603eea84da29ebcf20984fb1fdf6e602028/coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0", size = 244824, upload-time = "2025-05-23T11:38:59.421Z" }, 205 | { url = "https://files.pythonhosted.org/packages/26/45/36297a4c0cea4de2b2c442fe32f60c3991056c59cdc3cdd5346fbb995c97/coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050", size = 242872, upload-time = "2025-05-23T11:39:01.049Z" }, 206 | { url = "https://files.pythonhosted.org/packages/a4/71/e041f1b9420f7b786b1367fa2a375703889ef376e0d48de9f5723fb35f11/coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48", size = 244179, upload-time = "2025-05-23T11:39:02.709Z" }, 207 | { url = "https://files.pythonhosted.org/packages/bd/db/3c2bf49bdc9de76acf2491fc03130c4ffc51469ce2f6889d2640eb563d77/coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7", size = 214393, upload-time = "2025-05-23T11:39:05.457Z" }, 208 | { url = "https://files.pythonhosted.org/packages/c6/dc/947e75d47ebbb4b02d8babb1fad4ad381410d5bc9da7cfca80b7565ef401/coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3", size = 215194, upload-time = "2025-05-23T11:39:07.171Z" }, 209 | { url = "https://files.pythonhosted.org/packages/90/31/a980f7df8a37eaf0dc60f932507fda9656b3a03f0abf188474a0ea188d6d/coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7", size = 213580, upload-time = "2025-05-23T11:39:08.862Z" }, 210 | { url = "https://files.pythonhosted.org/packages/8a/6a/25a37dd90f6c95f59355629417ebcb74e1c34e38bb1eddf6ca9b38b0fc53/coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008", size = 212734, upload-time = "2025-05-23T11:39:11.109Z" }, 211 | { url = "https://files.pythonhosted.org/packages/36/8b/3a728b3118988725f40950931abb09cd7f43b3c740f4640a59f1db60e372/coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36", size = 212959, upload-time = "2025-05-23T11:39:12.751Z" }, 212 | { url = "https://files.pythonhosted.org/packages/53/3c/212d94e6add3a3c3f412d664aee452045ca17a066def8b9421673e9482c4/coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46", size = 257024, upload-time = "2025-05-23T11:39:15.569Z" }, 213 | { url = "https://files.pythonhosted.org/packages/a4/40/afc03f0883b1e51bbe804707aae62e29c4e8c8bbc365c75e3e4ddeee9ead/coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be", size = 252867, upload-time = "2025-05-23T11:39:17.64Z" }, 214 | { url = "https://files.pythonhosted.org/packages/18/a2/3699190e927b9439c6ded4998941a3c1d6fa99e14cb28d8536729537e307/coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740", size = 255096, upload-time = "2025-05-23T11:39:19.328Z" }, 215 | { url = "https://files.pythonhosted.org/packages/b4/06/16e3598b9466456b718eb3e789457d1a5b8bfb22e23b6e8bbc307df5daf0/coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625", size = 256276, upload-time = "2025-05-23T11:39:21.077Z" }, 216 | { url = "https://files.pythonhosted.org/packages/a7/d5/4b5a120d5d0223050a53d2783c049c311eea1709fa9de12d1c358e18b707/coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b", size = 254478, upload-time = "2025-05-23T11:39:22.838Z" }, 217 | { url = "https://files.pythonhosted.org/packages/ba/85/f9ecdb910ecdb282b121bfcaa32fa8ee8cbd7699f83330ee13ff9bbf1a85/coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199", size = 255255, upload-time = "2025-05-23T11:39:24.644Z" }, 218 | { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, 219 | { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, 220 | { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, 221 | { url = "https://files.pythonhosted.org/packages/69/2f/572b29496d8234e4a7773200dd835a0d32d9e171f2d974f3fe04a9dbc271/coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837", size = 203636, upload-time = "2025-05-23T11:39:52.002Z" }, 222 | { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, 223 | ] 224 | 225 | [package.optional-dependencies] 226 | toml = [ 227 | { name = "tomli", marker = "python_full_version <= '3.11'" }, 228 | ] 229 | 230 | [[package]] 231 | name = "cryptography" 232 | version = "45.0.3" 233 | source = { registry = "https://pypi.org/simple" } 234 | dependencies = [ 235 | { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, 236 | ] 237 | sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" } 238 | wheels = [ 239 | { url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" }, 240 | { url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" }, 241 | { url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" }, 242 | { url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" }, 243 | { url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" }, 244 | { url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" }, 245 | { url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" }, 246 | { url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" }, 247 | { url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" }, 248 | { url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" }, 249 | { url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" }, 250 | { url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" }, 251 | { url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" }, 252 | { url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" }, 253 | { url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" }, 254 | { url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" }, 255 | { url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" }, 256 | { url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" }, 257 | { url = "https://files.pythonhosted.org/packages/96/61/751ebea58c87b5be533c429f01996050a72c7283b59eee250275746632ea/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:555e5e2d3a53b4fabeca32835878b2818b3f23966a4efb0d566689777c5a12c8", size = 4146964, upload-time = "2025-05-25T14:17:09.538Z" }, 258 | { url = "https://files.pythonhosted.org/packages/8d/01/28c90601b199964de383da0b740b5156f5d71a1da25e7194fdf793d373ef/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:25286aacb947286620a31f78f2ed1a32cded7be5d8b729ba3fb2c988457639e4", size = 4388103, upload-time = "2025-05-25T14:17:11.978Z" }, 259 | { url = "https://files.pythonhosted.org/packages/3d/ec/cd892180b9e42897446ef35c62442f5b8b039c3d63a05f618aa87ec9ebb5/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:050ce5209d5072472971e6efbfc8ec5a8f9a841de5a4db0ebd9c2e392cb81972", size = 4150031, upload-time = "2025-05-25T14:17:14.131Z" }, 260 | { url = "https://files.pythonhosted.org/packages/db/d4/22628c2dedd99289960a682439c6d3aa248dff5215123ead94ac2d82f3f5/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:dc10ec1e9f21f33420cc05214989544727e776286c1c16697178978327b95c9c", size = 4387389, upload-time = "2025-05-25T14:17:17.303Z" }, 261 | ] 262 | 263 | [[package]] 264 | name = "distlib" 265 | version = "0.3.9" 266 | source = { registry = "https://pypi.org/simple" } 267 | sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } 268 | wheels = [ 269 | { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, 270 | ] 271 | 272 | [[package]] 273 | name = "docutils" 274 | version = "0.21.2" 275 | source = { registry = "https://pypi.org/simple" } 276 | sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } 277 | wheels = [ 278 | { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, 279 | ] 280 | 281 | [[package]] 282 | name = "fastapi" 283 | version = "0.109.2" 284 | source = { registry = "https://pypi.org/simple" } 285 | dependencies = [ 286 | { name = "pydantic" }, 287 | { name = "starlette" }, 288 | { name = "typing-extensions" }, 289 | ] 290 | sdist = { url = "https://files.pythonhosted.org/packages/01/d5/33a8992fe0e811211cd1cbc219cefa4732f9fb0555921346a59d1fec0040/fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73", size = 11720963, upload-time = "2024-02-04T21:26:10.672Z" } 291 | wheels = [ 292 | { url = "https://files.pythonhosted.org/packages/bf/97/60351307ab4502908d29f64f2801a36709a3f1888447bb328bc373d6ca0e/fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d", size = 92071, upload-time = "2024-02-04T21:26:07.478Z" }, 293 | ] 294 | 295 | [[package]] 296 | name = "fastmcp" 297 | version = "0.4.1" 298 | source = { registry = "https://pypi.org/simple" } 299 | dependencies = [ 300 | { name = "httpx" }, 301 | { name = "mcp" }, 302 | { name = "pydantic" }, 303 | { name = "pydantic-settings" }, 304 | { name = "python-dotenv" }, 305 | { name = "typer" }, 306 | ] 307 | sdist = { url = "https://files.pythonhosted.org/packages/6f/84/17b549133263d7ee77141970769bbc401525526bf1af043ea6842bce1a55/fastmcp-0.4.1.tar.gz", hash = "sha256:713ad3b8e4e04841c9e2f3ca022b053adb89a286ceffad0d69ae7b56f31cbe64", size = 785575, upload-time = "2024-12-09T13:33:11.101Z" } 308 | wheels = [ 309 | { url = "https://files.pythonhosted.org/packages/79/0b/008a340435fe8f0879e9d608f48af2737ad48440e09bd33b83b3fd03798b/fastmcp-0.4.1-py3-none-any.whl", hash = "sha256:664b42c376fb89ec90a50c9433f5a1f4d24f36696d6c41b024b427ae545f9619", size = 35282, upload-time = "2024-12-09T13:33:09.469Z" }, 310 | ] 311 | 312 | [[package]] 313 | name = "filelock" 314 | version = "3.18.0" 315 | source = { registry = "https://pypi.org/simple" } 316 | sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } 317 | wheels = [ 318 | { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, 319 | ] 320 | 321 | [[package]] 322 | name = "h11" 323 | version = "0.16.0" 324 | source = { registry = "https://pypi.org/simple" } 325 | sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } 326 | wheels = [ 327 | { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, 328 | ] 329 | 330 | [[package]] 331 | name = "httpcore" 332 | version = "1.0.9" 333 | source = { registry = "https://pypi.org/simple" } 334 | dependencies = [ 335 | { name = "certifi" }, 336 | { name = "h11" }, 337 | ] 338 | sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } 339 | wheels = [ 340 | { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, 341 | ] 342 | 343 | [[package]] 344 | name = "httpx" 345 | version = "0.28.1" 346 | source = { registry = "https://pypi.org/simple" } 347 | dependencies = [ 348 | { name = "anyio" }, 349 | { name = "certifi" }, 350 | { name = "httpcore" }, 351 | { name = "idna" }, 352 | ] 353 | 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" } 354 | wheels = [ 355 | { 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" }, 356 | ] 357 | 358 | [[package]] 359 | name = "httpx-sse" 360 | version = "0.4.0" 361 | source = { registry = "https://pypi.org/simple" } 362 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" } 363 | wheels = [ 364 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" }, 365 | ] 366 | 367 | [[package]] 368 | name = "id" 369 | version = "1.5.0" 370 | source = { registry = "https://pypi.org/simple" } 371 | dependencies = [ 372 | { name = "requests" }, 373 | ] 374 | sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237, upload-time = "2024-12-04T19:53:05.575Z" } 375 | wheels = [ 376 | { url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" }, 377 | ] 378 | 379 | [[package]] 380 | name = "identify" 381 | version = "2.6.12" 382 | source = { registry = "https://pypi.org/simple" } 383 | sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } 384 | wheels = [ 385 | { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, 386 | ] 387 | 388 | [[package]] 389 | name = "idna" 390 | version = "3.10" 391 | source = { registry = "https://pypi.org/simple" } 392 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } 393 | wheels = [ 394 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, 395 | ] 396 | 397 | [[package]] 398 | name = "importlib-metadata" 399 | version = "8.7.0" 400 | source = { registry = "https://pypi.org/simple" } 401 | dependencies = [ 402 | { name = "zipp" }, 403 | ] 404 | sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } 405 | wheels = [ 406 | { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, 407 | ] 408 | 409 | [[package]] 410 | name = "iniconfig" 411 | version = "2.1.0" 412 | source = { registry = "https://pypi.org/simple" } 413 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } 414 | wheels = [ 415 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, 416 | ] 417 | 418 | [[package]] 419 | name = "jaraco-classes" 420 | version = "3.4.0" 421 | source = { registry = "https://pypi.org/simple" } 422 | dependencies = [ 423 | { name = "more-itertools" }, 424 | ] 425 | sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } 426 | wheels = [ 427 | { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, 428 | ] 429 | 430 | [[package]] 431 | name = "jaraco-context" 432 | version = "6.0.1" 433 | source = { registry = "https://pypi.org/simple" } 434 | dependencies = [ 435 | { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, 436 | ] 437 | sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" } 438 | wheels = [ 439 | { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" }, 440 | ] 441 | 442 | [[package]] 443 | name = "jaraco-functools" 444 | version = "4.1.0" 445 | source = { registry = "https://pypi.org/simple" } 446 | dependencies = [ 447 | { name = "more-itertools" }, 448 | ] 449 | sdist = { url = "https://files.pythonhosted.org/packages/ab/23/9894b3df5d0a6eb44611c36aec777823fc2e07740dabbd0b810e19594013/jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", size = 19159, upload-time = "2024-09-27T19:47:09.122Z" } 450 | wheels = [ 451 | { url = "https://files.pythonhosted.org/packages/9f/4f/24b319316142c44283d7540e76c7b5a6dbd5db623abd86bb7b3491c21018/jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", size = 10187, upload-time = "2024-09-27T19:47:07.14Z" }, 452 | ] 453 | 454 | [[package]] 455 | name = "jeepney" 456 | version = "0.9.0" 457 | source = { registry = "https://pypi.org/simple" } 458 | sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } 459 | wheels = [ 460 | { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, 461 | ] 462 | 463 | [[package]] 464 | name = "keyring" 465 | version = "25.6.0" 466 | source = { registry = "https://pypi.org/simple" } 467 | dependencies = [ 468 | { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, 469 | { name = "jaraco-classes" }, 470 | { name = "jaraco-context" }, 471 | { name = "jaraco-functools" }, 472 | { name = "jeepney", marker = "sys_platform == 'linux'" }, 473 | { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, 474 | { name = "secretstorage", marker = "sys_platform == 'linux'" }, 475 | ] 476 | sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750, upload-time = "2024-12-25T15:26:45.782Z" } 477 | wheels = [ 478 | { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085, upload-time = "2024-12-25T15:26:44.377Z" }, 479 | ] 480 | 481 | [[package]] 482 | name = "markdown-it-py" 483 | version = "3.0.0" 484 | source = { registry = "https://pypi.org/simple" } 485 | dependencies = [ 486 | { name = "mdurl" }, 487 | ] 488 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } 489 | wheels = [ 490 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, 491 | ] 492 | 493 | [[package]] 494 | name = "mcp" 495 | version = "1.9.1" 496 | source = { registry = "https://pypi.org/simple" } 497 | dependencies = [ 498 | { name = "anyio" }, 499 | { name = "httpx" }, 500 | { name = "httpx-sse" }, 501 | { name = "pydantic" }, 502 | { name = "pydantic-settings" }, 503 | { name = "python-multipart" }, 504 | { name = "sse-starlette" }, 505 | { name = "starlette" }, 506 | { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, 507 | ] 508 | sdist = { url = "https://files.pythonhosted.org/packages/e7/bc/54aec2c334698cc575ca3b3481eed627125fb66544152fa1af927b1a495c/mcp-1.9.1.tar.gz", hash = "sha256:19879cd6dde3d763297617242888c2f695a95dfa854386a6a68676a646ce75e4", size = 316247, upload-time = "2025-05-22T15:52:21.26Z" } 509 | wheels = [ 510 | { url = "https://files.pythonhosted.org/packages/a6/c0/4ac795585a22a0a2d09cd2b1187b0252d2afcdebd01e10a68bbac4d34890/mcp-1.9.1-py3-none-any.whl", hash = "sha256:2900ded8ffafc3c8a7bfcfe8bc5204037e988e753ec398f371663e6a06ecd9a9", size = 130261, upload-time = "2025-05-22T15:52:19.702Z" }, 511 | ] 512 | 513 | [package.optional-dependencies] 514 | cli = [ 515 | { name = "python-dotenv" }, 516 | { name = "typer" }, 517 | ] 518 | 519 | [[package]] 520 | name = "mdurl" 521 | version = "0.1.2" 522 | source = { registry = "https://pypi.org/simple" } 523 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } 524 | wheels = [ 525 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, 526 | ] 527 | 528 | [[package]] 529 | name = "more-itertools" 530 | version = "10.7.0" 531 | source = { registry = "https://pypi.org/simple" } 532 | sdist = { url = "https://files.pythonhosted.org/packages/ce/a0/834b0cebabbfc7e311f30b46c8188790a37f89fc8d756660346fe5abfd09/more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3", size = 127671, upload-time = "2025-04-22T14:17:41.838Z" } 533 | wheels = [ 534 | { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" }, 535 | ] 536 | 537 | [[package]] 538 | name = "nh3" 539 | version = "0.2.21" 540 | source = { registry = "https://pypi.org/simple" } 541 | sdist = { url = "https://files.pythonhosted.org/packages/37/30/2f81466f250eb7f591d4d193930df661c8c23e9056bdc78e365b646054d8/nh3-0.2.21.tar.gz", hash = "sha256:4990e7ee6a55490dbf00d61a6f476c9a3258e31e711e13713b2ea7d6616f670e", size = 16581, upload-time = "2025-02-25T13:38:44.619Z" } 542 | wheels = [ 543 | { url = "https://files.pythonhosted.org/packages/7f/81/b83775687fcf00e08ade6d4605f0be9c4584cb44c4973d9f27b7456a31c9/nh3-0.2.21-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fcff321bd60c6c5c9cb4ddf2554e22772bb41ebd93ad88171bbbb6f271255286", size = 1297678, upload-time = "2025-02-25T13:37:56.063Z" }, 544 | { url = "https://files.pythonhosted.org/packages/22/ee/d0ad8fb4b5769f073b2df6807f69a5e57ca9cea504b78809921aef460d20/nh3-0.2.21-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31eedcd7d08b0eae28ba47f43fd33a653b4cdb271d64f1aeda47001618348fde", size = 733774, upload-time = "2025-02-25T13:37:58.419Z" }, 545 | { url = "https://files.pythonhosted.org/packages/ea/76/b450141e2d384ede43fe53953552f1c6741a499a8c20955ad049555cabc8/nh3-0.2.21-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d426d7be1a2f3d896950fe263332ed1662f6c78525b4520c8e9861f8d7f0d243", size = 760012, upload-time = "2025-02-25T13:38:01.017Z" }, 546 | { url = "https://files.pythonhosted.org/packages/97/90/1182275db76cd8fbb1f6bf84c770107fafee0cb7da3e66e416bcb9633da2/nh3-0.2.21-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9d67709bc0d7d1f5797b21db26e7a8b3d15d21c9c5f58ccfe48b5328483b685b", size = 923619, upload-time = "2025-02-25T13:38:02.617Z" }, 547 | { url = "https://files.pythonhosted.org/packages/29/c7/269a7cfbec9693fad8d767c34a755c25ccb8d048fc1dfc7a7d86bc99375c/nh3-0.2.21-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:55823c5ea1f6b267a4fad5de39bc0524d49a47783e1fe094bcf9c537a37df251", size = 1000384, upload-time = "2025-02-25T13:38:04.402Z" }, 548 | { url = "https://files.pythonhosted.org/packages/68/a9/48479dbf5f49ad93f0badd73fbb48b3d769189f04c6c69b0df261978b009/nh3-0.2.21-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:818f2b6df3763e058efa9e69677b5a92f9bc0acff3295af5ed013da544250d5b", size = 918908, upload-time = "2025-02-25T13:38:06.693Z" }, 549 | { url = "https://files.pythonhosted.org/packages/d7/da/0279c118f8be2dc306e56819880b19a1cf2379472e3b79fc8eab44e267e3/nh3-0.2.21-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b3b5c58161e08549904ac4abd450dacd94ff648916f7c376ae4b2c0652b98ff9", size = 909180, upload-time = "2025-02-25T13:38:10.941Z" }, 550 | { url = "https://files.pythonhosted.org/packages/26/16/93309693f8abcb1088ae143a9c8dbcece9c8f7fb297d492d3918340c41f1/nh3-0.2.21-cp313-cp313t-win32.whl", hash = "sha256:637d4a10c834e1b7d9548592c7aad760611415fcd5bd346f77fd8a064309ae6d", size = 532747, upload-time = "2025-02-25T13:38:12.548Z" }, 551 | { url = "https://files.pythonhosted.org/packages/a2/3a/96eb26c56cbb733c0b4a6a907fab8408ddf3ead5d1b065830a8f6a9c3557/nh3-0.2.21-cp313-cp313t-win_amd64.whl", hash = "sha256:713d16686596e556b65e7f8c58328c2df63f1a7abe1277d87625dcbbc012ef82", size = 528908, upload-time = "2025-02-25T13:38:14.059Z" }, 552 | { url = "https://files.pythonhosted.org/packages/ba/1d/b1ef74121fe325a69601270f276021908392081f4953d50b03cbb38b395f/nh3-0.2.21-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a772dec5b7b7325780922dd904709f0f5f3a79fbf756de5291c01370f6df0967", size = 1316133, upload-time = "2025-02-25T13:38:16.601Z" }, 553 | { url = "https://files.pythonhosted.org/packages/b8/f2/2c7f79ce6de55b41e7715f7f59b159fd59f6cdb66223c05b42adaee2b645/nh3-0.2.21-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d002b648592bf3033adfd875a48f09b8ecc000abd7f6a8769ed86b6ccc70c759", size = 758328, upload-time = "2025-02-25T13:38:18.972Z" }, 554 | { url = "https://files.pythonhosted.org/packages/6d/ad/07bd706fcf2b7979c51b83d8b8def28f413b090cf0cb0035ee6b425e9de5/nh3-0.2.21-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2a5174551f95f2836f2ad6a8074560f261cf9740a48437d6151fd2d4d7d617ab", size = 747020, upload-time = "2025-02-25T13:38:20.571Z" }, 555 | { url = "https://files.pythonhosted.org/packages/75/99/06a6ba0b8a0d79c3d35496f19accc58199a1fb2dce5e711a31be7e2c1426/nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b8d55ea1fc7ae3633d758a92aafa3505cd3cc5a6e40470c9164d54dff6f96d42", size = 944878, upload-time = "2025-02-25T13:38:22.204Z" }, 556 | { url = "https://files.pythonhosted.org/packages/79/d4/dc76f5dc50018cdaf161d436449181557373869aacf38a826885192fc587/nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ae319f17cd8960d0612f0f0ddff5a90700fa71926ca800e9028e7851ce44a6f", size = 903460, upload-time = "2025-02-25T13:38:25.951Z" }, 557 | { url = "https://files.pythonhosted.org/packages/cd/c3/d4f8037b2ab02ebf5a2e8637bd54736ed3d0e6a2869e10341f8d9085f00e/nh3-0.2.21-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ca02ac6f27fc80f9894409eb61de2cb20ef0a23740c7e29f9ec827139fa578", size = 839369, upload-time = "2025-02-25T13:38:28.174Z" }, 558 | { url = "https://files.pythonhosted.org/packages/11/a9/1cd3c6964ec51daed7b01ca4686a5c793581bf4492cbd7274b3f544c9abe/nh3-0.2.21-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f77e62aed5c4acad635239ac1290404c7e940c81abe561fd2af011ff59f585", size = 739036, upload-time = "2025-02-25T13:38:30.539Z" }, 559 | { url = "https://files.pythonhosted.org/packages/fd/04/bfb3ff08d17a8a96325010ae6c53ba41de6248e63cdb1b88ef6369a6cdfc/nh3-0.2.21-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:087ffadfdcd497658c3adc797258ce0f06be8a537786a7217649fc1c0c60c293", size = 768712, upload-time = "2025-02-25T13:38:32.992Z" }, 560 | { url = "https://files.pythonhosted.org/packages/9e/aa/cfc0bf545d668b97d9adea4f8b4598667d2b21b725d83396c343ad12bba7/nh3-0.2.21-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ac7006c3abd097790e611fe4646ecb19a8d7f2184b882f6093293b8d9b887431", size = 930559, upload-time = "2025-02-25T13:38:35.204Z" }, 561 | { url = "https://files.pythonhosted.org/packages/78/9d/6f5369a801d3a1b02e6a9a097d56bcc2f6ef98cffebf03c4bb3850d8e0f0/nh3-0.2.21-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:6141caabe00bbddc869665b35fc56a478eb774a8c1dfd6fba9fe1dfdf29e6efa", size = 1008591, upload-time = "2025-02-25T13:38:37.099Z" }, 562 | { url = "https://files.pythonhosted.org/packages/a6/df/01b05299f68c69e480edff608248313cbb5dbd7595c5e048abe8972a57f9/nh3-0.2.21-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:20979783526641c81d2f5bfa6ca5ccca3d1e4472474b162c6256745fbfe31cd1", size = 925670, upload-time = "2025-02-25T13:38:38.696Z" }, 563 | { url = "https://files.pythonhosted.org/packages/3d/79/bdba276f58d15386a3387fe8d54e980fb47557c915f5448d8c6ac6f7ea9b/nh3-0.2.21-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a7ea28cd49293749d67e4fcf326c554c83ec912cd09cd94aa7ec3ab1921c8283", size = 917093, upload-time = "2025-02-25T13:38:40.249Z" }, 564 | { url = "https://files.pythonhosted.org/packages/e7/d8/c6f977a5cd4011c914fb58f5ae573b071d736187ccab31bfb1d539f4af9f/nh3-0.2.21-cp38-abi3-win32.whl", hash = "sha256:6c9c30b8b0d291a7c5ab0967ab200598ba33208f754f2f4920e9343bdd88f79a", size = 537623, upload-time = "2025-02-25T13:38:41.893Z" }, 565 | { url = "https://files.pythonhosted.org/packages/23/fc/8ce756c032c70ae3dd1d48a3552577a325475af2a2f629604b44f571165c/nh3-0.2.21-cp38-abi3-win_amd64.whl", hash = "sha256:bb0014948f04d7976aabae43fcd4cb7f551f9f8ce785a4c9ef66e6c2590f8629", size = 535283, upload-time = "2025-02-25T13:38:43.355Z" }, 566 | ] 567 | 568 | [[package]] 569 | name = "nodeenv" 570 | version = "1.9.1" 571 | source = { registry = "https://pypi.org/simple" } 572 | sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } 573 | wheels = [ 574 | { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, 575 | ] 576 | 577 | [[package]] 578 | name = "packaging" 579 | version = "25.0" 580 | source = { registry = "https://pypi.org/simple" } 581 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } 582 | wheels = [ 583 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, 584 | ] 585 | 586 | [[package]] 587 | name = "platformdirs" 588 | version = "4.3.8" 589 | source = { registry = "https://pypi.org/simple" } 590 | sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } 591 | wheels = [ 592 | { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, 593 | ] 594 | 595 | [[package]] 596 | name = "pluggy" 597 | version = "1.6.0" 598 | source = { registry = "https://pypi.org/simple" } 599 | sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } 600 | wheels = [ 601 | { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, 602 | ] 603 | 604 | [[package]] 605 | name = "pre-commit" 606 | version = "3.6.2" 607 | source = { registry = "https://pypi.org/simple" } 608 | dependencies = [ 609 | { name = "cfgv" }, 610 | { name = "identify" }, 611 | { name = "nodeenv" }, 612 | { name = "pyyaml" }, 613 | { name = "virtualenv" }, 614 | ] 615 | sdist = { url = "https://files.pythonhosted.org/packages/04/bd/8a672a86e68f542c3f2ae17a9a8fa63babf16d1107be2f5290e5aa4369ba/pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e", size = 177293, upload-time = "2024-02-18T18:19:41.431Z" } 616 | wheels = [ 617 | { url = "https://files.pythonhosted.org/packages/f8/7c/f7a50d07ae9fa86d2149d4acb2daf61e7c0257b56bc1a24a7fb09c1b70df/pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c", size = 204185, upload-time = "2024-02-18T18:19:38.953Z" }, 618 | ] 619 | 620 | [[package]] 621 | name = "pycparser" 622 | version = "2.22" 623 | source = { registry = "https://pypi.org/simple" } 624 | sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } 625 | wheels = [ 626 | { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, 627 | ] 628 | 629 | [[package]] 630 | name = "pydantic" 631 | version = "2.11.5" 632 | source = { registry = "https://pypi.org/simple" } 633 | dependencies = [ 634 | { name = "annotated-types" }, 635 | { name = "pydantic-core" }, 636 | { name = "typing-extensions" }, 637 | { name = "typing-inspection" }, 638 | ] 639 | sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } 640 | wheels = [ 641 | { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, 642 | ] 643 | 644 | [[package]] 645 | name = "pydantic-core" 646 | version = "2.33.2" 647 | source = { registry = "https://pypi.org/simple" } 648 | dependencies = [ 649 | { name = "typing-extensions" }, 650 | ] 651 | sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } 652 | wheels = [ 653 | { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, 654 | { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, 655 | { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, 656 | { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, 657 | { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, 658 | { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, 659 | { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, 660 | { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, 661 | { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, 662 | { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, 663 | { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, 664 | { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, 665 | { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, 666 | { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, 667 | { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, 668 | { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, 669 | { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, 670 | { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, 671 | { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, 672 | { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, 673 | { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, 674 | { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, 675 | { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, 676 | { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, 677 | { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, 678 | { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, 679 | { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, 680 | { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, 681 | { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, 682 | { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, 683 | { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, 684 | { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, 685 | { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, 686 | { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, 687 | { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, 688 | { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, 689 | { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, 690 | { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, 691 | { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, 692 | { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, 693 | { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, 694 | { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, 695 | { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, 696 | { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, 697 | { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, 698 | { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, 699 | { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, 700 | { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, 701 | { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, 702 | { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, 703 | { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, 704 | { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, 705 | { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, 706 | { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, 707 | ] 708 | 709 | [[package]] 710 | name = "pydantic-settings" 711 | version = "2.9.1" 712 | source = { registry = "https://pypi.org/simple" } 713 | dependencies = [ 714 | { name = "pydantic" }, 715 | { name = "python-dotenv" }, 716 | { name = "typing-inspection" }, 717 | ] 718 | sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" } 719 | wheels = [ 720 | { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" }, 721 | ] 722 | 723 | [[package]] 724 | name = "pygments" 725 | version = "2.19.1" 726 | source = { registry = "https://pypi.org/simple" } 727 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } 728 | wheels = [ 729 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, 730 | ] 731 | 732 | [[package]] 733 | name = "pyproject-hooks" 734 | version = "1.2.0" 735 | source = { registry = "https://pypi.org/simple" } 736 | sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } 737 | wheels = [ 738 | { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, 739 | ] 740 | 741 | [[package]] 742 | name = "pytest" 743 | version = "8.0.0" 744 | source = { registry = "https://pypi.org/simple" } 745 | dependencies = [ 746 | { name = "colorama", marker = "sys_platform == 'win32'" }, 747 | { name = "iniconfig" }, 748 | { name = "packaging" }, 749 | { name = "pluggy" }, 750 | ] 751 | sdist = { url = "https://files.pythonhosted.org/packages/50/fd/af2d835eed57448960c4e7e9ab76ee42f24bcdd521e967191bc26fa2dece/pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c", size = 1395242, upload-time = "2024-01-27T21:47:58.099Z" } 752 | wheels = [ 753 | { url = "https://files.pythonhosted.org/packages/c7/10/727155d44c5e04bb08e880668e53079547282e4f950535234e5a80690564/pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6", size = 334024, upload-time = "2024-01-27T21:47:54.913Z" }, 754 | ] 755 | 756 | [[package]] 757 | name = "pytest-cov" 758 | version = "4.1.0" 759 | source = { registry = "https://pypi.org/simple" } 760 | dependencies = [ 761 | { name = "coverage", extra = ["toml"] }, 762 | { name = "pytest" }, 763 | ] 764 | sdist = { url = "https://files.pythonhosted.org/packages/7a/15/da3df99fd551507694a9b01f512a2f6cf1254f33601605843c3775f39460/pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", size = 63245, upload-time = "2023-05-24T18:44:56.845Z" } 765 | wheels = [ 766 | { url = "https://files.pythonhosted.org/packages/a7/4b/8b78d126e275efa2379b1c2e09dc52cf70df16fc3b90613ef82531499d73/pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a", size = 21949, upload-time = "2023-05-24T18:44:54.079Z" }, 767 | ] 768 | 769 | [[package]] 770 | name = "python-dotenv" 771 | version = "1.0.1" 772 | source = { registry = "https://pypi.org/simple" } 773 | sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } 774 | wheels = [ 775 | { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, 776 | ] 777 | 778 | [[package]] 779 | name = "python-multipart" 780 | version = "0.0.20" 781 | source = { registry = "https://pypi.org/simple" } 782 | sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } 783 | wheels = [ 784 | { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, 785 | ] 786 | 787 | [[package]] 788 | name = "pywin32-ctypes" 789 | version = "0.2.3" 790 | source = { registry = "https://pypi.org/simple" } 791 | sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } 792 | wheels = [ 793 | { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, 794 | ] 795 | 796 | [[package]] 797 | name = "pyyaml" 798 | version = "6.0.2" 799 | source = { registry = "https://pypi.org/simple" } 800 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } 801 | wheels = [ 802 | { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, 803 | { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, 804 | { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, 805 | { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, 806 | { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, 807 | { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, 808 | { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, 809 | { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, 810 | { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, 811 | { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, 812 | { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, 813 | { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, 814 | { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, 815 | { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, 816 | { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, 817 | { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, 818 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, 819 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, 820 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, 821 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, 822 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, 823 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, 824 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, 825 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, 826 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, 827 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, 828 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, 829 | ] 830 | 831 | [[package]] 832 | name = "readme-renderer" 833 | version = "44.0" 834 | source = { registry = "https://pypi.org/simple" } 835 | dependencies = [ 836 | { name = "docutils" }, 837 | { name = "nh3" }, 838 | { name = "pygments" }, 839 | ] 840 | sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } 841 | wheels = [ 842 | { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, 843 | ] 844 | 845 | [[package]] 846 | name = "requests" 847 | version = "2.31.0" 848 | source = { registry = "https://pypi.org/simple" } 849 | dependencies = [ 850 | { name = "certifi" }, 851 | { name = "charset-normalizer" }, 852 | { name = "idna" }, 853 | { name = "urllib3" }, 854 | ] 855 | sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794, upload-time = "2023-05-22T15:12:44.175Z" } 856 | wheels = [ 857 | { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574, upload-time = "2023-05-22T15:12:42.313Z" }, 858 | ] 859 | 860 | [[package]] 861 | name = "requests-toolbelt" 862 | version = "1.0.0" 863 | source = { registry = "https://pypi.org/simple" } 864 | dependencies = [ 865 | { name = "requests" }, 866 | ] 867 | sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } 868 | wheels = [ 869 | { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, 870 | ] 871 | 872 | [[package]] 873 | name = "rfc3986" 874 | version = "2.0.0" 875 | source = { registry = "https://pypi.org/simple" } 876 | sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } 877 | wheels = [ 878 | { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, 879 | ] 880 | 881 | [[package]] 882 | name = "rich" 883 | version = "14.0.0" 884 | source = { registry = "https://pypi.org/simple" } 885 | dependencies = [ 886 | { name = "markdown-it-py" }, 887 | { name = "pygments" }, 888 | ] 889 | sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } 890 | wheels = [ 891 | { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, 892 | ] 893 | 894 | [[package]] 895 | name = "ruff" 896 | version = "0.3.0" 897 | source = { registry = "https://pypi.org/simple" } 898 | sdist = { url = "https://files.pythonhosted.org/packages/61/b0/5fb64bc79464823ca94e566c9000143ddc11f9396c6e20202315059dd64f/ruff-0.3.0.tar.gz", hash = "sha256:0886184ba2618d815067cf43e005388967b67ab9c80df52b32ec1152ab49f53a", size = 2057870, upload-time = "2024-02-29T15:20:35.144Z" } 899 | wheels = [ 900 | { url = "https://files.pythonhosted.org/packages/db/70/438f8f5f2c10f175f8842ec55f20d526644c1acd6f1959f906d89f33df21/ruff-0.3.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7deb528029bacf845bdbb3dbb2927d8ef9b4356a5e731b10eef171e3f0a85944", size = 14893686, upload-time = "2024-02-29T15:19:26.139Z" }, 901 | { url = "https://files.pythonhosted.org/packages/b4/83/b20583af7e02d1b754bdbe9e044895a910745742a6684af89873bb02b604/ruff-0.3.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e1e0d4381ca88fb2b73ea0766008e703f33f460295de658f5467f6f229658c19", size = 7649013, upload-time = "2024-02-29T15:19:32.465Z" }, 902 | { url = "https://files.pythonhosted.org/packages/29/d5/38ba370224e73c9a0bdad42bfa1b4c8f1cadb000388d88224034bc7710b4/ruff-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f7dbba46e2827dfcb0f0cc55fba8e96ba7c8700e0a866eb8cef7d1d66c25dcb", size = 7314702, upload-time = "2024-02-29T15:19:36.009Z" }, 903 | { url = "https://files.pythonhosted.org/packages/f6/cf/6ae283997542bd13094912c5d962ec094f424df410966f7a980496e76fce/ruff-0.3.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23dbb808e2f1d68eeadd5f655485e235c102ac6f12ad31505804edced2a5ae77", size = 6734438, upload-time = "2024-02-29T15:19:39.822Z" }, 904 | { url = "https://files.pythonhosted.org/packages/f3/c7/6dd394f94fdd2e8647d1a09d77d73603d98be7bfccbf8838a336244b99bc/ruff-0.3.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ef655c51f41d5fa879f98e40c90072b567c666a7114fa2d9fe004dffba00932", size = 7794714, upload-time = "2024-02-29T15:19:43.706Z" }, 905 | { url = "https://files.pythonhosted.org/packages/8d/09/b61e354b1ead724286341581597bda2b7eab6dca395338080e872524618f/ruff-0.3.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d0d3d7ef3d4f06433d592e5f7d813314a34601e6c5be8481cccb7fa760aa243e", size = 8463708, upload-time = "2024-02-29T15:19:47.305Z" }, 906 | { url = "https://files.pythonhosted.org/packages/9d/07/d768b9c912c455ad6078aa2c5c9d2ba1289acaddc3e2d90b82d46da6131f/ruff-0.3.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b08b356d06a792e49a12074b62222f9d4ea2a11dca9da9f68163b28c71bf1dd4", size = 8199533, upload-time = "2024-02-29T15:19:51.603Z" }, 907 | { url = "https://files.pythonhosted.org/packages/22/99/a97f42dc36b0ee86854ef39f005e526fd0b5e7e23333edd1045559d0c020/ruff-0.3.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9343690f95710f8cf251bee1013bf43030072b9f8d012fbed6ad702ef70d360a", size = 8920967, upload-time = "2024-02-29T15:19:55.403Z" }, 908 | { url = "https://files.pythonhosted.org/packages/25/0b/801edb5c505339e08fbdea188f3dc28c033b4d7a8b320cbecbe6c8fae7fa/ruff-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1f3ed501a42f60f4dedb7805fa8d4534e78b4e196f536bac926f805f0743d49", size = 7831145, upload-time = "2024-02-29T15:19:58.902Z" }, 909 | { url = "https://files.pythonhosted.org/packages/43/9b/8ca336184128b022bbf09ba928240df8c17a9cebc08f34f9f638945ccd3e/ruff-0.3.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cc30a9053ff2f1ffb505a585797c23434d5f6c838bacfe206c0e6cf38c921a1e", size = 7238068, upload-time = "2024-02-29T15:20:03.855Z" }, 910 | { url = "https://files.pythonhosted.org/packages/09/46/fbef05ded40b91040e92050e429ea81c5180dec99e5a67fd850be28cb33c/ruff-0.3.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5da894a29ec018a8293d3d17c797e73b374773943e8369cfc50495573d396933", size = 6735986, upload-time = "2024-02-29T15:20:08.984Z" }, 911 | { url = "https://files.pythonhosted.org/packages/8c/21/ef5e8e4e0d5d27906e3400a196464d110b1ad11a80df9ec147e59c8e74ba/ruff-0.3.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:755c22536d7f1889be25f2baf6fedd019d0c51d079e8417d4441159f3bcd30c2", size = 7450367, upload-time = "2024-02-29T15:20:14.019Z" }, 912 | { url = "https://files.pythonhosted.org/packages/87/3f/d5360a01397c6ed85ebc54b69af8a85410bd02d2a5ee2843dce21861e12c/ruff-0.3.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd73fe7f4c28d317855da6a7bc4aa29a1500320818dd8f27df95f70a01b8171f", size = 7886340, upload-time = "2024-02-29T15:20:18.582Z" }, 913 | { url = "https://files.pythonhosted.org/packages/39/21/052d121336189425711eb769293896c1f17235d6357f7b6ef8a62c680497/ruff-0.3.0-py3-none-win32.whl", hash = "sha256:19eacceb4c9406f6c41af806418a26fdb23120dfe53583df76d1401c92b7c14b", size = 6908759, upload-time = "2024-02-29T15:20:22.298Z" }, 914 | { url = "https://files.pythonhosted.org/packages/6d/15/c3d5079dc3269d069fda09076ceae4f8608df079106eb298051d39f97bdb/ruff-0.3.0-py3-none-win_amd64.whl", hash = "sha256:128265876c1d703e5f5e5a4543bd8be47c73a9ba223fd3989d4aa87dd06f312f", size = 7596729, upload-time = "2024-02-29T15:20:26.973Z" }, 915 | { url = "https://files.pythonhosted.org/packages/6c/c6/31b49f2be455ae38205f70a6c48a69f04953d50351d62eb0e51a07b9b6f4/ruff-0.3.0-py3-none-win_arm64.whl", hash = "sha256:e3a4a6d46aef0a84b74fcd201a4401ea9a6cd85614f6a9435f2d33dd8cefbf83", size = 7215471, upload-time = "2024-02-29T15:20:31.117Z" }, 916 | ] 917 | 918 | [[package]] 919 | name = "secretstorage" 920 | version = "3.3.3" 921 | source = { registry = "https://pypi.org/simple" } 922 | dependencies = [ 923 | { name = "cryptography" }, 924 | { name = "jeepney" }, 925 | ] 926 | sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739, upload-time = "2022-08-13T16:22:46.976Z" } 927 | wheels = [ 928 | { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221, upload-time = "2022-08-13T16:22:44.457Z" }, 929 | ] 930 | 931 | [[package]] 932 | name = "setuptools" 933 | version = "80.9.0" 934 | source = { registry = "https://pypi.org/simple" } 935 | sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } 936 | wheels = [ 937 | { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, 938 | ] 939 | 940 | [[package]] 941 | name = "shellingham" 942 | version = "1.5.4" 943 | source = { registry = "https://pypi.org/simple" } 944 | sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } 945 | wheels = [ 946 | { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, 947 | ] 948 | 949 | [[package]] 950 | name = "sniffio" 951 | version = "1.3.1" 952 | source = { registry = "https://pypi.org/simple" } 953 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } 954 | wheels = [ 955 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, 956 | ] 957 | 958 | [[package]] 959 | name = "sse-starlette" 960 | version = "2.1.3" 961 | source = { registry = "https://pypi.org/simple" } 962 | dependencies = [ 963 | { name = "anyio" }, 964 | { name = "starlette" }, 965 | { name = "uvicorn" }, 966 | ] 967 | sdist = { url = "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz", hash = "sha256:9cd27eb35319e1414e3d2558ee7414487f9529ce3b3cf9b21434fd110e017169", size = 19678, upload-time = "2024-08-01T08:52:50.248Z" } 968 | wheels = [ 969 | { url = "https://files.pythonhosted.org/packages/52/aa/36b271bc4fa1d2796311ee7c7283a3a1c348bad426d37293609ca4300eef/sse_starlette-2.1.3-py3-none-any.whl", hash = "sha256:8ec846438b4665b9e8c560fcdea6bc8081a3abf7942faa95e5a744999d219772", size = 9383, upload-time = "2024-08-01T08:52:48.659Z" }, 970 | ] 971 | 972 | [[package]] 973 | name = "starlette" 974 | version = "0.36.3" 975 | source = { registry = "https://pypi.org/simple" } 976 | dependencies = [ 977 | { name = "anyio" }, 978 | ] 979 | sdist = { url = "https://files.pythonhosted.org/packages/be/47/1bba49d42d63f4453f0a64a20acbf2d0bd2f5a8cde6a166ee66c074a08f8/starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080", size = 2842113, upload-time = "2024-02-04T18:16:24.95Z" } 980 | wheels = [ 981 | { url = "https://files.pythonhosted.org/packages/eb/f7/372e3953b6e6fbfe0b70a1bb52612eae16e943f4288516480860fcd4ac41/starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044", size = 71481, upload-time = "2024-02-04T18:16:21.392Z" }, 982 | ] 983 | 984 | [[package]] 985 | name = "tomli" 986 | version = "2.2.1" 987 | source = { registry = "https://pypi.org/simple" } 988 | sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } 989 | wheels = [ 990 | { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, 991 | { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, 992 | { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, 993 | { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, 994 | { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, 995 | { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, 996 | { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, 997 | { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, 998 | { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, 999 | { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, 1000 | { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, 1001 | { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, 1002 | { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, 1003 | { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, 1004 | { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, 1005 | { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, 1006 | { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, 1007 | { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, 1008 | { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, 1009 | { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, 1010 | { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, 1011 | { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, 1012 | { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, 1013 | { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, 1014 | { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, 1015 | { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, 1016 | { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, 1017 | { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, 1018 | { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, 1019 | { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, 1020 | { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "tqdm" 1025 | version = "4.67.1" 1026 | source = { registry = "https://pypi.org/simple" } 1027 | dependencies = [ 1028 | { name = "colorama", marker = "sys_platform == 'win32'" }, 1029 | ] 1030 | sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } 1031 | wheels = [ 1032 | { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "twine" 1037 | version = "6.1.0" 1038 | source = { registry = "https://pypi.org/simple" } 1039 | dependencies = [ 1040 | { name = "id" }, 1041 | { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, 1042 | { name = "packaging" }, 1043 | { name = "readme-renderer" }, 1044 | { name = "requests" }, 1045 | { name = "requests-toolbelt" }, 1046 | { name = "rfc3986" }, 1047 | { name = "rich" }, 1048 | { name = "urllib3" }, 1049 | ] 1050 | sdist = { url = "https://files.pythonhosted.org/packages/c8/a2/6df94fc5c8e2170d21d7134a565c3a8fb84f9797c1dd65a5976aaf714418/twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd", size = 168404, upload-time = "2025-01-21T18:45:26.758Z" } 1051 | wheels = [ 1052 | { url = "https://files.pythonhosted.org/packages/7c/b6/74e927715a285743351233f33ea3c684528a0d374d2e43ff9ce9585b73fe/twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", size = 40791, upload-time = "2025-01-21T18:45:24.584Z" }, 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "typer" 1057 | version = "0.16.0" 1058 | source = { registry = "https://pypi.org/simple" } 1059 | dependencies = [ 1060 | { name = "click" }, 1061 | { name = "rich" }, 1062 | { name = "shellingham" }, 1063 | { name = "typing-extensions" }, 1064 | ] 1065 | sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } 1066 | wheels = [ 1067 | { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "typing-extensions" 1072 | version = "4.13.2" 1073 | source = { registry = "https://pypi.org/simple" } 1074 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } 1075 | wheels = [ 1076 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "typing-inspection" 1081 | version = "0.4.1" 1082 | source = { registry = "https://pypi.org/simple" } 1083 | dependencies = [ 1084 | { name = "typing-extensions" }, 1085 | ] 1086 | sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } 1087 | wheels = [ 1088 | { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "urllib3" 1093 | version = "2.4.0" 1094 | source = { registry = "https://pypi.org/simple" } 1095 | sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } 1096 | wheels = [ 1097 | { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "uvicorn" 1102 | version = "0.27.1" 1103 | source = { registry = "https://pypi.org/simple" } 1104 | dependencies = [ 1105 | { name = "click" }, 1106 | { name = "h11" }, 1107 | ] 1108 | sdist = { url = "https://files.pythonhosted.org/packages/09/d8/8aa69c76585035ca81851d99c3b00fd6be050aefd478a5376ff9fc5feb69/uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a", size = 41151, upload-time = "2024-02-10T12:09:11.325Z" } 1109 | wheels = [ 1110 | { url = "https://files.pythonhosted.org/packages/d9/fd/bac111726b6c651f1fa5563145ecba5ff70d36fb140a55e0d79b60b9d65e/uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4", size = 60809, upload-time = "2024-02-10T12:09:08.934Z" }, 1111 | ] 1112 | 1113 | [[package]] 1114 | name = "virtualenv" 1115 | version = "20.31.2" 1116 | source = { registry = "https://pypi.org/simple" } 1117 | dependencies = [ 1118 | { name = "distlib" }, 1119 | { name = "filelock" }, 1120 | { name = "platformdirs" }, 1121 | ] 1122 | sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } 1123 | wheels = [ 1124 | { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "wavespeed-mcp" 1129 | version = "0.1.23" 1130 | source = { editable = "." } 1131 | dependencies = [ 1132 | { name = "fastapi" }, 1133 | { name = "httpx" }, 1134 | { name = "mcp", extra = ["cli"] }, 1135 | { name = "pydantic" }, 1136 | { name = "python-dotenv" }, 1137 | { name = "requests" }, 1138 | { name = "setuptools" }, 1139 | { name = "tqdm" }, 1140 | { name = "uvicorn" }, 1141 | ] 1142 | 1143 | [package.optional-dependencies] 1144 | dev = [ 1145 | { name = "build" }, 1146 | { name = "fastmcp" }, 1147 | { name = "pre-commit" }, 1148 | { name = "pytest" }, 1149 | { name = "pytest-cov" }, 1150 | { name = "ruff" }, 1151 | { name = "twine" }, 1152 | ] 1153 | 1154 | [package.metadata] 1155 | requires-dist = [ 1156 | { name = "build", marker = "extra == 'dev'", specifier = ">=1.0.3" }, 1157 | { name = "fastapi", specifier = "==0.109.2" }, 1158 | { name = "fastmcp", marker = "extra == 'dev'", specifier = "==0.4.1" }, 1159 | { name = "httpx", specifier = "==0.28.1" }, 1160 | { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, 1161 | { name = "pre-commit", marker = "extra == 'dev'", specifier = "==3.6.2" }, 1162 | { name = "pydantic", specifier = ">=2.6.1" }, 1163 | { name = "pytest", marker = "extra == 'dev'", specifier = "==8.0.0" }, 1164 | { name = "pytest-cov", marker = "extra == 'dev'", specifier = "==4.1.0" }, 1165 | { name = "python-dotenv", specifier = "==1.0.1" }, 1166 | { name = "requests", specifier = "==2.31.0" }, 1167 | { name = "ruff", marker = "extra == 'dev'", specifier = "==0.3.0" }, 1168 | { name = "setuptools", specifier = ">=80.9.0" }, 1169 | { name = "tqdm", specifier = ">=4.66.0" }, 1170 | { name = "twine", marker = "extra == 'dev'", specifier = "==6.1.0" }, 1171 | { name = "uvicorn", specifier = "==0.27.1" }, 1172 | ] 1173 | provides-extras = ["dev"] 1174 | 1175 | [[package]] 1176 | name = "zipp" 1177 | version = "3.22.0" 1178 | source = { registry = "https://pypi.org/simple" } 1179 | sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257, upload-time = "2025-05-26T14:46:32.217Z" } 1180 | wheels = [ 1181 | { url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796, upload-time = "2025-05-26T14:46:30.775Z" }, 1182 | ] 1183 | --------------------------------------------------------------------------------