diff --git a/plugins/dative/.gitignore b/plugins/dative/.gitignore new file mode 100644 index 000000000..ed871cba8 --- /dev/null +++ b/plugins/dative/.gitignore @@ -0,0 +1,14 @@ +__pycache__ +.benchmarks +.idea +.mypy_cache +.pytest_cache +.ropeproject +.ruff_cache +.venv +.vscode +.zed + +datasets/bird_minidev +.coverage +.DS_Store diff --git a/plugins/dative/Dockerfile b/plugins/dative/Dockerfile new file mode 100644 index 000000000..585dcde61 --- /dev/null +++ b/plugins/dative/Dockerfile @@ -0,0 +1,34 @@ +FROM astral/uv:0.8-python3.11-bookworm-slim AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends gcc libc-dev + +ENV UV_COMPILE_BYTECODE=1 UV_NO_INSTALLER_METADATA=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=0 + +# Install dependencies +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + --mount=type=bind,source=scripts/install_duckdb_extensions.py,target=install_duckdb_extensions.py \ + uv sync --frozen --no-install-project --no-dev \ + && uv run python install_duckdb_extensions.py + +# clean the cache +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --frozen --no-install-project --no-dev + +FROM python:3.11-slim-bookworm AS runtime + +COPY --from=builder --chown=dative:dative /.venv /.venv +COPY --from=builder --chown=dative:dative /root/.duckdb /root/.duckdb + +COPY src/dative /dative + +COPY docker/entrypoint.sh / + +ENV PATH="/.venv/bin:$PATH" + +EXPOSE 3000 + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/plugins/dative/Justfile b/plugins/dative/Justfile new file mode 100644 index 000000000..19f4b04a8 --- /dev/null +++ b/plugins/dative/Justfile @@ -0,0 +1,44 @@ +# 安装项目依赖库 +install: + uv sync + +U: + uv sync --upgrade + +up package: + uv sync --upgrade-package {{ package }} + +# 项目代码风格格式化 +format: + uv run ruff format src tests/unit + +# 执行项目checklist +check: + uv run ruff check --fix src tests/unit + +sqlfmt: install + uv run scripts/sqlfmt.py src/dative/core/data_source/metadata_sql + +# 项目类型检查 +type: + uv run mypy src + +# 执行单元测试 +test: + uv run pytest tests/unit + +# 执行功能测试 +test_function: + uv run pytest tests/function + +# 执行集成测试 +test_integration: + uv run pytest tests/integration + +ci: install format check type test + +dev: install + uv run fastapi dev src/dative/main.py + +run: install + uv run fastapi run src/dative/main.py diff --git a/plugins/dative/README.md b/plugins/dative/README.md new file mode 100644 index 000000000..8fa6f1ef8 --- /dev/null +++ b/plugins/dative/README.md @@ -0,0 +1,32 @@ +输入自然语言,生成sql查询,给出自然语言回答 + +## 特性: +- 支持mysql、postgresql、sqlite等数据库 +- 结合duckdb强大本地数据库管理能力,支持本地和S3存储结构化数据,如:xlsx、xls、csv、xlsm、xlsb等 +- 支持sql基本语法检查和优化 +- 支持单个数据库查询,不支持跨数据库查询 +- 仅支持sql查询,不支持更新、删除、插入等语句 + + +## 本地开发 + +1、项目管理工具使用 [uv](https://github.com/astral-sh/uv),使用pip安装: +```bash +pip install uv +``` + +2、安装依赖包: +``` +uv sync +``` + +3、安装duckdb扩展包: +```bash +uv run python scripts/install_duckdb_extensions.py +``` + +4、启动服务: +```bash +uv run fastapi run src/dative/main.py +``` + diff --git a/plugins/dative/docker/entrypoint.sh b/plugins/dative/docker/entrypoint.sh new file mode 100644 index 000000000..00471e96e --- /dev/null +++ b/plugins/dative/docker/entrypoint.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e + +# Function to check if environment is development (case-insensitive) +is_development() { + local env + env=$(echo "${DATIVE_ENVIRONMENT}" | tr '[:upper:]' '[:lower:]') + [[ "$env" == "development" ]] +} + +start_arq() { + echo "Starting ARQ worker..." + # Use exec to replace the shell with arq_worker if running in single process mode + if ! is_development; then + exec arq_worker + else + arq_worker & + ARQ_PID=$! + echo "ARQ worker started with PID: $ARQ_PID" + fi +} + +start_uvicorn() { + echo "Starting Uvicorn server..." + # Use PORT environment variable, default to 3000 if not set + local port="${PORT:-3000}" + # Use exec to replace the shell with uvicorn if running in single process mode + if ! is_development; then + exec uvicorn dative.main:app --host '0.0.0.0' --port "$port" + else + uvicorn dative.main:app --host '0.0.0.0' --port "$port" & + UVICORN_PID=$! + echo "Uvicorn server started with PID: $UVICORN_PID" + fi +} + +# Start the appropriate service +if [[ "${MODE}" == "worker" ]]; then + start_arq +else + start_uvicorn +fi + +if is_development; then + sleep infinity +else + echo "Signal handling disabled. Process will replace shell (exec)." + # In this case, start_arq or start_uvicorn would have used exec + # So we shouldn't reach here unless something went wrong +fi diff --git a/plugins/dative/pyproject.toml b/plugins/dative/pyproject.toml new file mode 100644 index 000000000..381038bf5 --- /dev/null +++ b/plugins/dative/pyproject.toml @@ -0,0 +1,110 @@ +[project] +name = "dative" +version = "0.1.0" +description = "Query data from database by natural language" +requires-python = "~=3.11.0" +dependencies = [ + "aiobotocore>=2.24.2", + "aiosqlite>=0.21.0", + "asyncpg>=0.30.0", + "cryptography>=45.0.6", + "duckdb>=1.3.2", + "fastapi<1.0", + "fastexcel>=0.15.1", + "greenlet>=3.2.4", + "httpx[socks]>=0.28.1", + "json-repair>=0.50.1", + "langchain-core<1.0", + "langchain-openai<1.0", + "mysql-connector-python>=9.4.0", + "orjson>=3.11.2", + "pyarrow>=21.0.0", + "pydantic-settings>=2.10.1", + "python-dateutil>=2.9.0.post0", + "python-dotenv>=1.1.1", + "pytz>=2025.2", + "sqlglot[rs]==27.12.0", + "uvicorn[standard]<1.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.sdist] +only-include = ["src/"] + +[tool.hatch.build.targets.wheel] +packages = ["src/"] + +[tool.uv] +index-url = "https://pypi.tuna.tsinghua.edu.cn/simple" +extra-index-url = ["https://mirrors.aliyun.com/pypi/simple"] + +[dependency-groups] +dev = [ + "hatch>=1.14.1", + "ruff>=0.12.10", + "pytest-async>=0.1.1", + "pytest-asyncio>=0.26.0", + "pytest-cov>=6.3.0", + "pytest-mock>=3.14.0", + "pytest>=8.3.3", + "pytest-benchmark>=4.0.0", + "fastapi-cli>=0.0.8", + "mypy>=1.17.1", + "types-python-dateutil>=2.9.0.20250822", + "pyarrow-stubs>=20.0.0.20250825", + "asyncpg-stubs>=0.30.2", + "boto3-stubs>=1.40.25", + "types-aiobotocore[essential]>=2.24.2", +] + +[tool.uv.sources] + +# checklist +[tool.ruff] +line-length = 120 +target-version = "py311" +preview = true + +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "F", # Pyflakes + "W", # Warning + "N", # PEP8 Naming + "I", # isort + "FAST", # FastAPI +] +# 禁用的规则 +ignore = [] + +[tool.sqruff] +output_line_length = 120 +max_line_length = 120 + +# 类型 +[tool.mypy] +mypy_path = "./src" +exclude = ["tests/"] + +# 单元测试 +[tool.pytest.ini_options] +addopts = ["-rA", "--cov=dative"] +testpaths = ["tests/unit"] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +python_files = ["*.py"] + +# 代码覆盖率 +[tool.coverage.run] +branch = true +parallel = true +omit = ["**/__init__.py"] + +[tool.coverage.report] +# fail_under = 85 +show_missing = true +sort = "cover" +exclude_lines = ["no cov", "if __name__ == .__main__.:"] diff --git a/plugins/dative/scripts/install_duckdb_extensions.py b/plugins/dative/scripts/install_duckdb_extensions.py new file mode 100644 index 000000000..185638c28 --- /dev/null +++ b/plugins/dative/scripts/install_duckdb_extensions.py @@ -0,0 +1,8 @@ +import duckdb + +extensions = [ + "excel", + "httpfs" +] +for ext in extensions: + duckdb.install_extension(ext) diff --git a/plugins/dative/scripts/sqlfmt.py b/plugins/dative/scripts/sqlfmt.py new file mode 100644 index 000000000..e5aed4897 --- /dev/null +++ b/plugins/dative/scripts/sqlfmt.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +import argparse +import sys +from pathlib import Path + +from sqlglot import transpile +from sqlglot.dialects.dialect import DIALECT_MODULE_NAMES +from sqlglot.errors import ParseError + + +def transpile_sql_file(file: Path, dialect: str) -> None: + if dialect not in DIALECT_MODULE_NAMES: + raise ValueError(f"Dialect {dialect} not supported") + + if not file.is_file() or not file.suffix == ".sql": + print("Please specify a sql file") + return + + sql_txt = file.read_text(encoding="utf-8") + try: + sqls = transpile(sql_txt, read=dialect, write=dialect, pretty=True, indent=4, pad=4) + file.write_text(";\n\n".join(sqls) + "\n", encoding="utf-8") + except ParseError as e: + print(str(e)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="") + parser.add_argument("files_or_dirs", nargs='+', type=str, default=None, help="files or directories") + parser.add_argument('-d', '--dialect', type=str, help='输出文件') + args = parser.parse_args() + + if args.files_or_dirs: + for file_or_dir in args.files_or_dirs: + fd = Path(file_or_dir) + if fd.is_file(): + dialect = args.dialect or fd.stem.split(".")[-1] + transpile_sql_file(fd, dialect) + elif fd.is_dir(): + for sql_file in fd.glob("*.sql"): + dialect = args.dialect or sql_file.stem.split(".")[-1] + transpile_sql_file(sql_file, dialect) + else: + print(f"{file_or_dir} is not a sql file or a directory") + sys.exit(3) + else: + print("Please specify a file or a directory") + sys.exit(3) diff --git a/plugins/dative/src/dative/__init__.py b/plugins/dative/src/dative/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/plugins/dative/src/dative/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/plugins/dative/src/dative/api/v1/__init__.py b/plugins/dative/src/dative/api/v1/__init__.py new file mode 100644 index 000000000..908e32224 --- /dev/null +++ b/plugins/dative/src/dative/api/v1/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +from fastapi import APIRouter + +from . import data_source + +router = APIRouter() + +router.include_router(data_source.router, prefix="/data_source", tags=["data_source"]) diff --git a/plugins/dative/src/dative/api/v1/data_model.py b/plugins/dative/src/dative/api/v1/data_model.py new file mode 100644 index 000000000..469003bd2 --- /dev/null +++ b/plugins/dative/src/dative/api/v1/data_model.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +import os +from typing import Any, Literal, Union + +from pydantic import BaseModel, Field, SecretStr + +from dative.core.data_source import DatabaseMetadata + + +class DatabaseConfigBase(BaseModel): + host: str = Field(description="host") + port: int = Field(default=3306, description="port") + username: str = Field(description="username") + password: str = Field(description="password") + + +class SqliteConfig(BaseModel): + type: Literal["sqlite"] = "sqlite" + db_path: str = Field(description="db_path") + + +class MysqlConfig(DatabaseConfigBase): + type: Literal["mysql", "maria"] = "mysql" + db_name: str = Field(description="数据库名称") + conn_pool_size: int = Field(default=3, description="数据库连接池大小") + + +class PostgresConfig(DatabaseConfigBase): + type: Literal["postgres"] = "postgres" + db_name: str = Field(description="数据库名称") + ns_name: str = Field(default="public", alias="schema", description="ns_name") + conn_pool_size: int = Field(default=3, description="数据库连接池大小") + + +class DuckdbLocalStoreConfig(BaseModel): + type: Literal["local"] = Field(default="local", description="数据库存储方式") + dir_path: str = Field(description="Excel文件目录") + + +class DuckdbS3StoreConfig(BaseModel): + type: Literal["s3"] = Field(default="s3", description="数据库存储方式") + host: str = Field(description="host") + port: int = Field(default=3306, description="port") + access_key: str = Field(description="access_key") + secret_key: str = Field(description="secret_key") + bucket: str = Field(description="bucket") + region: str = Field(default="", description="region") + use_ssl: bool = Field(default=False, description="use_ssl") + + +class DuckdbConfig(BaseModel): + type: Literal["duckdb"] = "duckdb" + store: DuckdbLocalStoreConfig | DuckdbS3StoreConfig = Field(description="数据库存储方式", discriminator="type") + + +DataSourceConfig = Union[SqliteConfig, MysqlConfig, PostgresConfig, DuckdbConfig] + + +def default_api_key() -> SecretStr: + if os.getenv("AIPROXY_API_TOKEN"): + api_key = SecretStr(str(os.getenv("AIPROXY_API_TOKEN"))) + else: + api_key = SecretStr("") + return api_key + + +def default_base_url() -> str: + if os.getenv("AIPROXY_API_ENDPOINT"): + base_url = str(os.getenv("AIPROXY_API_ENDPOINT")) + "/v1" + else: + base_url = "https://api.openai.com/v1" + return base_url + + +class LLMInfo(BaseModel): + provider: Literal["openai"] = Field(default="openai", description="LLM提供者") + model: str = Field(description="LLM模型名称") + api_key: SecretStr = Field(default_factory=default_api_key, description="API密钥", examples=["sk-..."]) + base_url: str = Field( + default_factory=default_base_url, description="API基础URL", examples=["https://api.openai.com/v1"] + ) + temperature: float = Field(default=0.7, description="温度参数") + max_tokens: int | None = Field(default=None, description="最大生成长度") + extra_body: dict[str, Any] | None = Field(default=None) + + +class SqlQueryRequest(BaseModel): + source_config: DataSourceConfig = Field(description="数据库连接信息", discriminator="type") + sql: str + + +class SqlQueryResponse(BaseModel): + cols: list[str] = Field(default=list(), description="查询结果列") + data: list[tuple[Any, ...]] = Field(default=list(), description="查询结果数据") + + +class QueryByNLRequest(BaseModel): + source_config: DataSourceConfig = Field(description="数据库连接信息", discriminator="type") + query: str = Field(description="用户问题") + retrieved_metadata: DatabaseMetadata = Field(description="检索到的元数据") + generate_sql_llm: LLMInfo = Field(description="生成sql的LLM配置信息") + evaluate_sql_llm: LLMInfo | None = Field(default=None, description="评估sql的LLM配置信息") + schema_format: Literal["markdown", "m_schema"] = Field(default="markdown", description="schema转成prompt格式") + evidence: str = Field(default="", description="补充说明信息") + result_num_limit: int = Field(default=100, description="结果数量限制") + + +class QueryByNLResponse(BaseModel): + answer: str = Field(description="生成的答案") + sql: str = Field(description="生成的SQL语句") + sql_res: SqlQueryResponse = Field(description="SQL查询结果") + input_tokens: int = Field(description="输入token数") + output_tokens: int = Field(description="输出token数") diff --git a/plugins/dative/src/dative/api/v1/data_source.py b/plugins/dative/src/dative/api/v1/data_source.py new file mode 100644 index 000000000..3107d21a0 --- /dev/null +++ b/plugins/dative/src/dative/api/v1/data_source.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- + +import traceback +from typing import Annotated + +from fastapi import APIRouter, Depends, HTTPException +from fastapi.responses import ORJSONResponse +from langchain_openai import ChatOpenAI + +from dative.api.v1.data_model import ( + DataSourceConfig, + QueryByNLRequest, + QueryByNLResponse, + SqlQueryRequest, + SqlQueryResponse, +) +from dative.core.agent import Agent +from dative.core.data_source import ( + DatabaseMetadata, + DataSourceBase, + DBServerVersion, + DuckdbLocalStore, + DuckdbS3Store, + Mysql, + Postgres, + Sqlite, +) + +router = APIRouter() + + +async def valid_data_source_config(source_config: DataSourceConfig) -> DataSourceBase: + ds: DataSourceBase + if source_config.type == "mysql" or source_config.type == "maria": + ds = Mysql( + host=source_config.host, + port=source_config.port, + username=source_config.username, + password=source_config.password, + db_name=source_config.db_name, + ) + elif source_config.type == "postgres": + ds = Postgres( + host=source_config.host, + port=source_config.port, + username=source_config.username, + password=source_config.password, + db_name=source_config.db_name, + schema=source_config.ns_name, + ) + elif source_config.type == "duckdb": + if source_config.store.type == "local": + ds = DuckdbLocalStore(dir_path=source_config.store.dir_path) + elif source_config.store.type == "s3": + ds = DuckdbS3Store( + host=source_config.store.host, + port=source_config.store.port, + access_key=source_config.store.access_key, + secret_key=source_config.store.secret_key, + bucket=source_config.store.bucket, + region=source_config.store.region, + use_ssl=source_config.store.use_ssl, + ) + else: + raise HTTPException( + status_code=400, + detail={ + "msg": f"Unsupported duckdb storage type: {source_config.store}", + "error": f"Unsupported duckdb storage type: {source_config.store}", + }, + ) + elif source_config.type == "sqlite": + ds = Sqlite(source_config.db_path) + else: + raise HTTPException( + status_code=400, + detail={ + "msg": f"Unsupported data source types: {source_config.type}", + "error": f"Unsupported data source types: {source_config.type}", + }, + ) + try: + await ds.conn_test() + return ds + except Exception as e: + raise HTTPException( + status_code=400, + detail={ + "msg": "Connection failed. Please check the connection information.", + "error": str(e), + }, + ) + + +@router.post("/conn_test", response_class=ORJSONResponse, dependencies=[Depends(valid_data_source_config)]) +async def conn_test() -> str: + return "ok" + + +@router.post("/get_metadata", response_class=ORJSONResponse) +async def get_metadata(ds: Annotated[DataSourceBase, Depends(valid_data_source_config)]) -> DatabaseMetadata: + try: + return await ds.aget_metadata() + except Exception as e: + raise HTTPException( + status_code=400, + detail={ + "msg": "Failed to obtain database metadata", + "error": str(e), + }, + ) + +@router.post("/get_metadata_with_value_examples", response_class=ORJSONResponse) +async def get_metadata_with_value_example(ds: Annotated[DataSourceBase, Depends(valid_data_source_config)]) -> DatabaseMetadata: + try: + return await ds.aget_metadata_with_value_examples() + except Exception as e: + raise HTTPException( + status_code=400, + detail={ + "msg": "Failed to obtain database metadata", + "error": str(e), + }, + ) + +@router.post("/get_server_version", response_class=ORJSONResponse) +async def get_server_version(ds: Annotated[DataSourceBase, Depends(valid_data_source_config)]) -> DBServerVersion: + try: + return await ds.aget_server_version() + except Exception as e: + raise HTTPException( + status_code=400, + detail={ + "msg": "Failed to obtain database version information", + "error": str(e), + }, + ) + + +@router.post("/sql_query", response_class=ORJSONResponse) +async def sql_query(request: SqlQueryRequest) -> SqlQueryResponse: + ds = await valid_data_source_config(source_config=request.source_config) + cols, data, err = await ds.aexecute_raw_sql(sql=request.sql) + if err: + raise HTTPException( + status_code=400, + detail={ + "msg": "Database query failed", + "error": err.msg, + }, + ) + return SqlQueryResponse(cols=cols, data=data) + + +@router.post("/query_by_nl", response_class=ORJSONResponse) +async def query_by_nl(request: QueryByNLRequest) -> QueryByNLResponse: + ds = await valid_data_source_config(source_config=request.source_config) + try: + server_version = await ds.aget_server_version() + except Exception as e: + print(traceback.format_exc()) + raise HTTPException( + status_code=400, + detail={ + "msg": "Connection failed. Please check the connection information.", + "error": str(e), + }, + ) + generate_sql_llm = ChatOpenAI( + model=request.generate_sql_llm.model, + api_key=request.generate_sql_llm.api_key, + base_url=request.generate_sql_llm.base_url, + stream_usage=True, + extra_body=request.generate_sql_llm.extra_body + ) + if request.evaluate_sql_llm: + evaluate_sql_llm = ChatOpenAI( + model=request.evaluate_sql_llm.model, + api_key=request.evaluate_sql_llm.api_key, + base_url=request.evaluate_sql_llm.base_url, + stream_usage=True, + extra_body=request.evaluate_sql_llm.extra_body + ) + else: + evaluate_sql_llm = None + agent = Agent( + ds=ds, + db_server_version=server_version, + generate_sql_llm=generate_sql_llm, + result_num_limit=request.result_num_limit, + evaluate_sql_llm=evaluate_sql_llm, + ) + try: + answer, sql, cols, data, total_input_tokens, total_output_tokens = await agent.arun( + query=request.query, metadata=request.retrieved_metadata, evidence=request.evidence + ) + except Exception as e: + print(traceback.format_exc()) + raise HTTPException( + status_code=400, + detail={ + "msg": "Database search failed", + "error": str(e), + }, + ) + return QueryByNLResponse( + answer=answer, + sql=sql, + sql_res=SqlQueryResponse(cols=cols, data=data), + input_tokens=total_input_tokens, + output_tokens=total_output_tokens, + ) diff --git a/plugins/dative/src/dative/config/__init__.py b/plugins/dative/src/dative/config/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/plugins/dative/src/dative/config/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/plugins/dative/src/dative/config/settings.py b/plugins/dative/src/dative/config/settings.py new file mode 100644 index 000000000..234cc6cc4 --- /dev/null +++ b/plugins/dative/src/dative/config/settings.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from typing import ClassVar + +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict( + env_file=".env", + env_prefix="DATIVE_", + env_file_encoding="utf-8", + extra="ignore", + env_nested_delimiter="__", + nested_model_default_partial_update=True, + ) + + DATABASE_URL: str = "" + log_level: str = "INFO" + log_format: str = "plain" + redis_url: str = "" + + +settings = Settings() diff --git a/plugins/dative/src/dative/core/__init__.py b/plugins/dative/src/dative/core/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/plugins/dative/src/dative/core/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/plugins/dative/src/dative/core/agent.py b/plugins/dative/src/dative/core/agent.py new file mode 100644 index 000000000..e6fb5bbc8 --- /dev/null +++ b/plugins/dative/src/dative/core/agent.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +from typing import Any, Literal, Optional + +from langchain_core.language_models import BaseChatModel +from sqlglot.errors import SqlglotError + +from . import sql_generation as sql_gen +from . import sql_res_evaluation as sql_eval +from .data_source import DatabaseMetadata, DataSourceBase, DBServerVersion, SQLError +from .sql_inspection import SQLCheck, SQLOptimization + + +class Agent: + def __init__( + self, + ds: DataSourceBase, + db_server_version: DBServerVersion, + generate_sql_llm: BaseChatModel, + result_num_limit: int = 100, + schema_format: Literal["markdown", "m_schema"] = "m_schema", + max_attempts: int = 2, + evaluate_sql_llm: Optional[BaseChatModel] = None, + ): + self.ds = ds + self.generate_sql_llm = generate_sql_llm + self.schema_format = schema_format + self.db_server_version = db_server_version + self.sql_check = SQLCheck(ds.dialect) + self.sql_opt = SQLOptimization(dialect=ds.dialect, db_major_version=db_server_version.major) + self.result_num_limit = result_num_limit + self.max_attempts = max_attempts + self.evaluate_sql_llm = evaluate_sql_llm + + async def arun( + self, query: str, metadata: DatabaseMetadata, evidence: str = "" + ) -> tuple[str, str, list[str], list[tuple[Any, ...]], int, int]: + answer = None + sql = "" + cols: list[str] = [] + data: list[tuple[Any, ...]] = [] + total_input_tokens = 0 + total_output_tokens = 0 + attempts = 0 + + schema_str = f"# Database server info: {self.ds.dialect} {self.db_server_version}\n" + if self.schema_format == "markdown": + schema_str += metadata.to_markdown() + else: + schema_str += metadata.to_m_schema() + schema_type = {table.name: table.column_type() for table in metadata.tables if table.enabled} + current_sql, error_msg = None, None + while attempts < self.max_attempts: + answer, sql, input_tokens, output_tokens, error_msg = await sql_gen.arun( + query=query, + llm=self.generate_sql_llm, + db_info=schema_str, + evidence=evidence, + error_sql=current_sql, + error_msg=error_msg, + ) + total_input_tokens += input_tokens + total_output_tokens += output_tokens + if answer is not None: + return answer, sql, cols, data, total_input_tokens, total_output_tokens + elif error_msg is None and sql: + try: + sql_exp = self.sql_check.syntax_valid(sql) + if not self.sql_check.is_query(sql_exp): + return answer or "", sql, cols, data, total_input_tokens, total_output_tokens + + sql = self.sql_opt.arun(sql_exp, schema_type=schema_type, result_num_limit=self.result_num_limit) + except SqlglotError as e: + error_msg = f"SQL语法错误:{e}" + + if not error_msg: + cols, data, err = await self.ds.aexecute_raw_sql(sql) + if err: + if err.error_type != SQLError.SyntaxError: + return answer or "", sql, cols, data, total_input_tokens, total_output_tokens + else: + error_msg = err.msg + + if not error_msg and self.evaluate_sql_llm: + answer, input_tokens, output_tokens, error_msg = await sql_eval.arun( + query=query, + llm=self.evaluate_sql_llm, + db_info=schema_str, + sql=sql, + res_rows=data, + evidence=evidence, + ) + total_input_tokens += input_tokens + total_output_tokens += output_tokens + + if not error_msg: + break + attempts += 1 + current_sql = sql + + return answer or "", sql, cols, data, total_input_tokens, total_output_tokens diff --git a/plugins/dative/src/dative/core/data_source/__init__.py b/plugins/dative/src/dative/core/data_source/__init__.py new file mode 100644 index 000000000..3c9c82a21 --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/__init__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from .base import ( + DatabaseMetadata, + DataSourceBase, + DBException, + DBServerVersion, + DBTable, + SQLError, + SQLException, + TableColumn, + TableForeignKey, +) +from .duckdb_ds import DuckdbLocalStore, DuckdbS3Store +from .mysql_ds import Mysql +from .postgres_ds import Postgres +from .sqlite_ds import Sqlite + +__all__ = [ + "DataSourceBase", + "DatabaseMetadata", + "DBServerVersion", + "TableColumn", + "TableForeignKey", + "DBTable", + "SQLError", + "SQLException", + "Mysql", + "Postgres", + "DBException", + "DuckdbLocalStore", + "DuckdbS3Store", + "Sqlite", +] diff --git a/plugins/dative/src/dative/core/data_source/base.py b/plugins/dative/src/dative/core/data_source/base.py new file mode 100644 index 000000000..c7fe13dbf --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/base.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +from abc import ABC, abstractmethod +from enum import StrEnum, auto +from typing import Any, cast + +import orjson +from pydantic import BaseModel, Field +from sqlglot import exp + +from dative.core.utils import convert_value2str, is_date, is_email, is_number, is_valid_uuid, truncate_text + + +class TableColumn(BaseModel): + name: str = Field(description="列名") + type: str = Field(description="数据类型") + comment: str = Field(default="", description="描述信息") + auto_increment: bool = Field(default=False, description="是否自增") + nullable: bool = Field(default=True, description="是否允许为空") + default: Any = Field(default=None, description="默认值") + examples: list[Any] = Field(default_factory=list, description="值样例") + enabled: bool = Field(default=True, description="是否启用") + value_index: bool = Field(default=False, description="是否启用值索引") + + +class ConstraintKey(BaseModel): + name: str = Field(description="约束名") + column: str = Field(description="约束字段名") + + +class TableForeignKey(ConstraintKey): + referenced_schema: str = Field(description="引用schema") + referenced_table: str = Field(description="引用表名") + referenced_column: str = Field(description="引用字段名") + + +class DBTable(BaseModel): + name: str = Field(description="表名") + ns_name: str | None = Field(default=None, alias="schema", description="ns_name") + comment: str = Field(default="", description="描述信息") + columns: dict[str, TableColumn] = Field(default_factory=dict, description="列") + primary_keys: list[str] = Field(default_factory=list, description="主键") + foreign_keys: list[TableForeignKey] = Field(default_factory=list, description="外键") + enabled: bool = Field(default=True, description="是否启用") + + def column_type(self, case_insensitive: bool = True) -> dict[str, str]: + schema_type: dict[str, str] = dict() + for col in self.columns.values(): + schema_type[col.name] = col.type + if case_insensitive: + schema_type[col.name.title()] = col.type + schema_type[col.name.lower()] = col.type + schema_type[col.name.upper()] = col.type + return schema_type + + def to_markdown(self) -> str: + s = f""" +### Table name: {self.name} +{self.comment} +#### Columns: +|Name|Description|Type|Examples| +|---|---|---|---| +""" + for col in self.columns.values(): + if not col.enabled: + continue + + example_values = "
".join([convert_value2str(v) for v in col.examples]) + s += f"|{col.name}|{col.comment}|{col.type}|{example_values}|\n" + + if self.primary_keys: + s += f"#### Primary Keys: {tuple(self.primary_keys)}\n" + if self.foreign_keys: + fk_str = "#### Foreign Keys: \n" + for fk in self.foreign_keys: + # 只显示同一个schema的外键 + if self.ns_name is None or self.ns_name == fk.referenced_schema: + fk_str += f" - {fk.column} -> {fk.referenced_table}.{fk.referenced_column}\n" + s += fk_str + + return s + + def to_m_schema(self, db_name: str | None = None) -> str: + # XiYanSQL: https://github.com/XGenerationLab/M-Schema + output = [] + if db_name: + table_comment = f"# Table: {db_name}.{self.name}" + else: + table_comment = f"# Table: {self.name}" + if self.comment: + table_comment += f", {self.comment}" + output.append(table_comment) + + field_lines = [] + for col in self.columns.values(): + if not col.enabled: + continue + + field_line = f"({col.name}: {col.type.upper()}" + if col.name in self.primary_keys: + field_line += ", Primary Key" + if col.comment: + field_line += f", {col.comment.strip()}" + if col.examples: + example_values = ", ".join([convert_value2str(v) for v in col.examples]) + field_line += f", Examples: [{example_values}]" + field_line += ")" + field_lines.append(field_line) + + output.append("[") + output.append(",\n".join(field_lines)) + output.append("]") + return "\n".join(output) + + +class SQLError(StrEnum): + EmptySQL = auto() + SyntaxError = auto() + NotAllowedOperation = auto() + DBError = auto() + UnknownError = auto() + + +class SQLException(BaseModel): + error_type: SQLError + msg: str + code: int = Field(default=0, description="错误码") + + +class DatabaseMetadata(BaseModel): + name: str = Field(description="数据库名") + comment: str = Field(default="", description="描述信息") + tables: list[DBTable] = Field(default_factory=list, description="表") + + def to_markdown(self) -> str: + s = f""" +# Database name: {self.name} +{self.comment} +## Tables: +""" + for table in self.tables: + if table.enabled: + s += f"{table.to_markdown()}\n" + return s + + def to_m_schema(self) -> str: + output = [f"【DB_ID】 {self.name}"] + if self.comment: + output.append(self.comment) + output.append("【Schema】") + output.append("schema format: (column name: data type, is primary key, comment, examples)") + fks = [] + for t in self.tables: + if not t.enabled: + continue + output.append(t.to_m_schema(self.name)) + + for fk in t.foreign_keys: + # 只显示同一个schema的外键 + if t.ns_name is None or t.ns_name == fk.referenced_schema: + fks.append(f"{t.name}.{fk.column}={fk.referenced_table}.{fk.referenced_column}") + + if fks: + output.append("【Foreign Keys】") + output.extend(fks) + output.append("\n") + return "\n".join(output) + + +class DBServerVersion(BaseModel): + major: int = Field(description="主版本号") + minor: int = Field(description="次版本号") + patch: int | None = Field(default=None, description="修订号/补丁号") + + def __str__(self) -> str: + s = f"{self.major}.{self.minor}" + if self.patch is None: + return s + return s + f".{self.patch}" + + def __repr__(self) -> str: + return str(self) + + +class DBException(BaseModel): + code: int = Field(description="异常码") + msg: str = Field(description="异常信息") + + +class DataSourceBase(ABC): + @property + @abstractmethod + def dialect(self) -> str: + """return the type of data source""" + raise NotImplementedError + + @property + @abstractmethod + def string_types(self) -> set[str]: + """return the string types of data source""" + raise NotImplementedError + + @property + @abstractmethod + def json_array_agg_func(self) -> str: + """ + 获取JSON数组聚合函数的SQL表达式 + + Returns: + str: 返回适用于当前数据库类型的JSON数组聚合函数名称 + """ + raise NotImplementedError + + async def conn_test(self) -> bool: + await self.aexecute_raw_sql("SELECT 1") + return True + + @abstractmethod + async def aget_server_version(self) -> DBServerVersion: + """get server version""" + + @abstractmethod + async def aget_metadata(self) -> DatabaseMetadata: + """get database metadata""" + + @abstractmethod + async def aexecute_raw_sql(self, sql: str) -> tuple[list[str], list[tuple[Any, ...]], SQLException | None]: + """""" + + @staticmethod + async def distinct_values_exp(table_name: str, col_name: str, limit: int = 3) -> exp.Expression: + sql_exp = ( + exp.select(exp.column(col_name)) + .distinct() + .from_(exp.to_identifier(table_name)) + .where(exp.not_(exp.column(col_name).is_(exp.null()))) + .limit(limit) + ) + return sql_exp + + async def aget_metadata_with_value_examples( + self, value_num: int = 3, value_str_max_length: int = 40 + ) -> DatabaseMetadata: + """ + 获取数据库元数据及示例值信息 + + Args: + value_num (int): 每个字段需要获取的示例值数量,默认为3 + value_str_max_length (int): 字符串类型示例值的最大长度,默认为40 + + Returns: + DatabaseMetadata: 包含数据库元数据和示例值信息的对象 + """ + db_metadata = await self.aget_metadata() + + for t in db_metadata.tables: + col_exp = [] + for col in t.columns.values(): + col_exp.append(await self.agg_distinct_values(t.name, col.name, value_num)) + + sql = exp.union(*col_exp, distinct=False).sql(dialect=self.dialect) + _, rows, _ = await self.aexecute_raw_sql(sql) + rows = cast(list[tuple[str, str]], rows) + for i in range(len(rows)): + col_name = rows[i][0] + if rows[i][1]: + examples = orjson.loads(cast(str, rows[i][1])) + str_examples = [ + truncate_text(convert_value2str(v), max_length=value_str_max_length) for v in examples + ] + t.columns[col_name].examples = str_examples + if ( + t.columns[col_name].type in self.string_types + and not is_valid_uuid(str_examples[0]) + and not is_number(str_examples[0]) + and not is_date(str_examples[0]) + and not is_email(str_examples[0]) + ): + t.columns[col_name].value_index = True + + return db_metadata + + async def agg_distinct_values(self, table_name: str, col_name: str, limit: int = 3) -> exp.Expression: + dis_exp = await self.distinct_values_exp(table_name, col_name, limit) + sql_exp = exp.select( + exp.Alias(this=exp.Literal.string(col_name), alias="name"), + exp.Alias(this=exp.func(self.json_array_agg_func, exp.column(col_name)), alias="examples"), + ).from_(exp.Subquery(this=dis_exp, alias="t")) + return sql_exp diff --git a/plugins/dative/src/dative/core/data_source/duckdb_ds.py b/plugins/dative/src/dative/core/data_source/duckdb_ds.py new file mode 100644 index 000000000..9a21c9ee0 --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/duckdb_ds.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +from abc import ABC, abstractmethod +from pathlib import Path +from typing import Any, cast + +import duckdb +import orjson +import pyarrow as pa +from aiobotocore.session import get_session +from botocore.exceptions import ClientError +from fastexcel import read_excel + +from .base import DatabaseMetadata, DataSourceBase, DBServerVersion, SQLError, SQLException + +with open(Path(__file__).parent / "metadata_sql" / "duckdb.sql", encoding="utf-8") as f: + METADATA_SQL = f.read() + +supported_file_extensions = {"xlsx", "xls", "xlsm", "xlsb", "csv", "json", "parquet"} + + +class DuckdbBase(DataSourceBase, ABC): + """ + duckdb 和 polars对比:polars目前不支持对excel的sql查询优化( 懒加载api:pl.scan_ ) + 在文件较大,数据量较多时,通过sql查询优化不用把数据全部加载到内存进行查询。 + """ + + def __init__(self, db_name: str): + self.db_name = db_name + self.conn = self.get_conn() + self.loaded = False + + @abstractmethod + def get_conn(self) -> duckdb.DuckDBPyConnection: + """""" + + @property + def dialect(self) -> str: + return "duckdb" + + @property + def string_types(self) -> set[str]: + return {"VARCHAR", "CHAR", "BPCHAR", "TEXT", "STRING"} + + @property + def json_array_agg_func(self) -> str: + return "JSON_GROUP_ARRAY" + + async def aget_server_version(self) -> DBServerVersion: + version = [int(i) for i in duckdb.__version__.split(".") if i.isdigit()] + server_version = DBServerVersion(major=version[0], minor=version[1]) + if len(version) > 2: + server_version.patch = version[2] + return server_version + + @abstractmethod + async def read_files(self) -> list[tuple[str, str]]: + """""" + + async def load_data(self) -> None: + if self.loaded: + return + + # 加载新数据,重置连接 + self.conn = self.get_conn() + files = await self.read_files() + for name, file_path in files: + extension = Path(file_path).suffix.split(".")[-1].lower() + if extension == "xlsx": + sql = f"select * from read_xlsx('{file_path}')" + self.conn.register(f"{name}", self.conn.sql(sql)) + elif extension == "csv": + sql = f"select * from read_csv('{file_path}')" + self.conn.register(f"{name}", self.conn.sql(sql)) + elif extension == "json": + sql = f"select * from read_json('{file_path}')" + self.conn.register(f"{name}", self.conn.sql(sql)) + elif extension == "parquet": + sql = f"select * from read_parquet('{file_path}')" + self.conn.register(f"{name}", self.conn.sql(sql)) + else: + wb = read_excel(file_path) + record_batch = wb.load_sheet(wb.sheet_names[0]).to_arrow() + self.conn.register(f"{name}", pa.Table.from_batches([record_batch])) + self.loaded = True + + async def aexecute_raw_sql(self, sql: str) -> tuple[list[str], list[tuple[Any, ...]], SQLException | None]: + try: + df: duckdb.DuckDBPyRelation = self.conn.sql(sql) + return df.columns, df.fetchall(), None + except duckdb.Error as e: + return [], [], SQLException(error_type=SQLError.SyntaxError, msg=str(e)) + + async def aget_metadata(self) -> DatabaseMetadata: + await self.load_data() + + _, rows, err = await self.aexecute_raw_sql(METADATA_SQL.format(db_name=self.db_name)) + if err: + raise ConnectionError(err.msg) + if not rows or not rows[0][1]: + return DatabaseMetadata(name=self.db_name) + metadata = DatabaseMetadata.model_validate({ + "name": self.db_name, + "tables": orjson.loads(cast(str, rows[0][1])), + }) + return metadata + + +class DuckdbLocalStore(DuckdbBase): + def __init__(self, dir_path: str | Path): + if isinstance(dir_path, str): + dir_path = Path(dir_path) + super().__init__(dir_path.stem) + + self.dir_path = dir_path + self.conn = self.get_conn() + self.loaded = False + + def get_conn(self) -> duckdb.DuckDBPyConnection: + conn = duckdb.connect(config={"allow_unsigned_extensions": True}) + conn.load_extension("excel") + return conn + + async def conn_test(self) -> bool: + if self.dir_path.is_dir(): + await self.load_data() + return True + return False + + async def read_files(self) -> list[tuple[str, str]]: + excel_files: list[tuple[str, str]] = [] + for file_path in self.dir_path.glob("*"): + if file_path.is_file() and file_path.suffix.split(".")[-1].lower() in supported_file_extensions: + excel_files.append((file_path.stem, str(file_path.absolute()))) + + return excel_files + + +class DuckdbS3Store(DuckdbBase): + def __init__( + self, + host: str, + port: int, + access_key: str, + secret_key: str, + bucket: str, + region: str = "", + use_ssl: bool = False, + ): + self.host = host + self.port = port + self.endpoint = f"{self.host}:{self.port}" + self.access_key = access_key + self.secret_key = secret_key + self.bucket = bucket + self.db_name = bucket + self.region = region + self.use_ssl = use_ssl + self.conn = self.get_conn() + super().__init__(self.db_name) + + if self.use_ssl: + self.endpoint_url = f"https://{self.endpoint}" + else: + self.endpoint_url = f"http://{self.endpoint}" + self.session = get_session() + + def get_conn(self) -> duckdb.DuckDBPyConnection: + conn = duckdb.connect() + conn.load_extension("httpfs") + sql = f""" +CREATE OR REPLACE SECRET secret ( + TYPE s3, + ENDPOINT '{self.endpoint}', + KEY_ID '{self.access_key}', + SECRET '{self.secret_key}', + USE_SSL {orjson.dumps(self.use_ssl).decode()}, + URL_STYLE 'path', + REGION '{self.region!r}' +); +""" + conn.execute(sql) + return conn + + async def conn_test(self) -> bool: + try: + async with self.session.create_client( + service_name="s3", + endpoint_url="http://localhost:9000", + aws_secret_access_key=self.secret_key, + aws_access_key_id=self.access_key, + region_name=self.region, + ) as client: + await client.head_bucket(Bucket=self.bucket) + return True + except ClientError: + return False + + async def read_files(self) -> list[tuple[str, str]]: + excel_files: list[tuple[str, str]] = [] + async with self.session.create_client( + service_name="s3", + endpoint_url="http://localhost:9000", + aws_secret_access_key=self.secret_key, + aws_access_key_id=self.access_key, + region_name=self.region, + ) as client: + paginator = client.get_paginator("list_objects_v2") + async for result in paginator.paginate(Bucket=self.bucket): + for obj in result.get("Contents", []): + if obj["Key"].split(".")[-1].lower() in supported_file_extensions: + excel_files.append((obj["Key"].split(".")[0], f"s3://{self.bucket}/{obj['Key']}")) + return excel_files diff --git a/plugins/dative/src/dative/core/data_source/metadata_sql/duckdb.sql b/plugins/dative/src/dative/core/data_source/metadata_sql/duckdb.sql new file mode 100644 index 000000000..06aef85b9 --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/metadata_sql/duckdb.sql @@ -0,0 +1,19 @@ +SELECT + '{db_name}' AS db_name, + JSON_GROUP_ARRAY(JSON_OBJECT('name', t.table_name, 'columns', t.columns)) AS tables +FROM ( + SELECT + t.table_name, + JSON_GROUP_OBJECT( + t.column_name, + JSON_OBJECT( + 'name', t.column_name, + 'default', t.column_default, + 'nullable', CASE WHEN t.is_nullable = 'YES' THEN TRUE ELSE FALSE END, + 'type', UPPER(t.data_type) + ) + ) AS columns + FROM information_schema.columns AS t + GROUP BY + t.table_name +) AS t diff --git a/plugins/dative/src/dative/core/data_source/metadata_sql/mysql.sql b/plugins/dative/src/dative/core/data_source/metadata_sql/mysql.sql new file mode 100644 index 000000000..065aa06ab --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/metadata_sql/mysql.sql @@ -0,0 +1,91 @@ +SELECT + t.table_schema, + JSON_ARRAYAGG( + JSON_OBJECT( + 'name', t.table_name, + 'columns', t.columns, + 'primary_keys', COALESCE(t.primary_keys, JSON_ARRAY()), + 'foreign_keys', COALESCE(t.foreign_keys, JSON_ARRAY()), + 'comment', t.table_comment + ) + ) AS tables +FROM ( + SELECT + t1.table_schema, + t1.table_name, + t1.table_comment, + t2.columns, + t3.primary_keys, + t4.foreign_keys + FROM information_schema.tables AS t1 + JOIN ( + SELECT + t.table_schema, + t.table_name, + JSON_OBJECTAGG( + t.column_name, JSON_OBJECT( + 'name', t.column_name, + 'default', t.column_default, + 'nullable', CASE WHEN t.is_nullable = 'YES' THEN CAST(TRUE AS JSON) ELSE CAST(FALSE AS JSON) END, + 'type', UPPER(t.data_type), + 'auto_increment', CASE + WHEN t.extra = 'auto_increment' + THEN CAST(TRUE AS JSON) + ELSE CAST(FALSE AS JSON) + END, + 'comment', t.column_comment + ) + ) AS columns + FROM information_schema.columns AS t + WHERE + table_schema = '{db_name}' + GROUP BY + t.table_schema, + t.table_name + ) AS t2 + ON t1.table_schema = t2.table_schema AND t1.table_name = t2.table_name + LEFT JOIN ( + SELECT + kcu.table_schema, + kcu.table_name, + JSON_ARRAYAGG(kcu.column_name) AS primary_keys + FROM information_schema.key_column_usage AS kcu + JOIN information_schema.table_constraints AS tc + ON kcu.table_schema = '{db_name}' + AND kcu.constraint_name = tc.constraint_name + AND kcu.constraint_schema = tc.constraint_schema + AND kcu.table_name = tc.table_name + AND tc.constraint_type = 'PRIMARY KEY' + GROUP BY + kcu.table_schema, + kcu.table_name + ) AS t3 + ON t1.table_schema = t3.table_schema AND t1.table_name = t3.table_name + LEFT JOIN ( + SELECT + kcu.table_schema, + kcu.table_name, + JSON_ARRAYAGG( + JSON_OBJECT( + 'name', kcu.constraint_name, + 'column', kcu.column_name, + 'referenced_schema', kcu.referenced_table_schema, + 'referenced_table', kcu.referenced_table_name, + 'referenced_column', kcu.referenced_column_name + ) + ) AS foreign_keys + FROM information_schema.key_column_usage AS kcu + JOIN information_schema.table_constraints AS tc + ON kcu.table_schema = '{db_name}' + AND kcu.constraint_name = tc.constraint_name + AND kcu.constraint_schema = tc.constraint_schema + AND kcu.table_name = tc.table_name + AND tc.constraint_type = 'FOREIGN KEY' + GROUP BY + kcu.table_schema, + kcu.table_name + ) AS t4 + ON t1.table_schema = t4.table_schema AND t1.table_name = t4.table_name +) AS t +GROUP BY + t.table_schema diff --git a/plugins/dative/src/dative/core/data_source/metadata_sql/postgres.sql b/plugins/dative/src/dative/core/data_source/metadata_sql/postgres.sql new file mode 100644 index 000000000..bb4d7f60c --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/metadata_sql/postgres.sql @@ -0,0 +1,174 @@ +SELECT + t.table_schema, + JSON_AGG( + JSON_BUILD_OBJECT( + 'name', + t.table_name, + 'schema', + t.table_schema, + 'columns', + t.columns, + 'primary_keys', + COALESCE(t.primary_keys, CAST('[]' AS JSON)), + 'foreign_keys', + COALESCE(t.foreign_keys, CAST('[]' AS JSON)), + 'comment', + t.comment + ) + ) AS tables +FROM ( + SELECT + t1.table_schema, + t1.table_name, + t1.comment, + t2.columns, + t3.primary_keys, + t4.foreign_keys + FROM ( + SELECT + n.nspname AS table_schema, + c.relname AS table_name, + COALESCE(OBJ_DESCRIPTION(c.oid), '') AS comment + FROM pg_class AS c + JOIN pg_namespace AS n + ON n.oid = c.relnamespace AND c.relkind = 'r' AND n.nspname = '{schema}' + ) AS t1 + JOIN ( + SELECT + t.table_schema, + t.table_name, + JSON_OBJECT_AGG( + t.column_name, + JSON_BUILD_OBJECT( + 'name', + t.column_name, + 'default', + t.column_default, + 'nullable', + CASE WHEN t.is_nullable = 'YES' THEN TRUE ELSE FALSE END, + 'type', + UPPER(t.data_type), + 'auto_increment', + t.auto_increment, + 'comment', + COALESCE(t.comment, '') + ) + ) AS columns + FROM ( + SELECT + c.table_schema, + c.table_name, + c.column_name, + c.data_type, + c.column_default, + c.is_nullable, + COALESCE(coldesc.comment, '') AS comment, + CASE + WHEN c.is_identity = 'YES' + OR c.column_default LIKE 'nextval%' + OR c.column_default LIKE 'nextval(%' + THEN TRUE + ELSE FALSE + END AS auto_increment + FROM information_schema.columns AS c + JOIN ( + SELECT + n.nspname AS table_schema, + c.relname AS table_name, + a.attname AS column_name, + COL_DESCRIPTION(c.oid, a.attnum) AS comment + FROM pg_class AS c + JOIN pg_namespace AS n + ON n.oid = c.relnamespace + JOIN pg_attribute AS a + ON a.attrelid = c.oid + WHERE + a.attnum > 0 AND NOT a.attisdropped AND n.nspname = '{schema}' + ) AS coldesc + ON c.table_schema = '{schema}' + AND c.table_schema = coldesc.table_schema + AND c.table_name = coldesc.table_name + AND c.column_name = coldesc.column_name + ) AS t + GROUP BY + t.table_schema, + t.table_name + ) AS t2 + ON t1.table_schema = t2.table_schema AND t1.table_name = t2.table_name + LEFT JOIN ( + SELECT + CAST(CAST(connamespace AS REGNAMESPACE) AS TEXT) AS table_schema, + CASE + WHEN POSITION('.' IN CAST(CAST(conrelid AS REGCLASS) AS TEXT)) > 0 + THEN SPLIT_PART(CAST(CAST(conrelid AS REGCLASS) AS TEXT), '.', 2) + ELSE CAST(CAST(conrelid AS REGCLASS) AS TEXT) + END AS table_name, + TO_JSON(STRING_TO_ARRAY(SUBSTRING(PG_GET_CONSTRAINTDEF(oid) FROM '\((.*?)\)'), ',')) AS primary_keys + FROM pg_constraint + WHERE + contype = 'p' AND CAST(CAST(connamespace AS REGNAMESPACE) AS TEXT) = '{schema}' + ) AS t3 + ON t1.table_schema = t3.table_schema AND t1.table_name = t3.table_name + LEFT JOIN ( + SELECT + t.table_schema, + t.table_name, + JSON_AGG( + JSON_BUILD_OBJECT( + 'name', + t.name, + 'column', + t.column_name, + 'referenced_schema', + t.referenced_table_schema, + 'referenced_table', + t.referenced_table_name, + 'referenced_column', + t.referenced_column_name + ) + ) AS foreign_keys + FROM ( + SELECT + c.conname AS name, + n.nspname AS table_schema, + CASE + WHEN POSITION('.' IN CAST(CAST(conrelid AS REGCLASS) AS TEXT)) > 0 + THEN SPLIT_PART(CAST(CAST(conrelid AS REGCLASS) AS TEXT), '.', 2) + ELSE CAST(CAST(conrelid AS REGCLASS) AS TEXT) + END AS TABLE_NAME, + A.attname AS COLUMN_NAME, + nr.nspname AS referenced_table_schema, + CASE + WHEN POSITION('.' IN CAST(CAST(confrelid AS REGCLASS) AS TEXT)) > 0 + THEN SPLIT_PART(CAST(CAST(confrelid AS REGCLASS) AS TEXT), '.', 2) + ELSE CAST(CAST(confrelid AS REGCLASS) AS TEXT) + END AS referenced_table_name, + af.attname AS referenced_column_name + FROM pg_constraint AS c + JOIN pg_attribute AS a + ON a.attnum = ANY( + c.conkey + ) AND a.attrelid = c.conrelid + JOIN pg_class AS cl + ON cl.oid = c.conrelid + JOIN pg_namespace AS n + ON n.oid = cl.relnamespace + JOIN pg_attribute AS af + ON af.attnum = ANY( + c.confkey + ) AND af.attrelid = c.confrelid + JOIN pg_class AS clf + ON clf.oid = c.confrelid + JOIN pg_namespace AS nr + ON nr.oid = clf.relnamespace + WHERE + c.contype = 'f' AND CAST(CAST(connamespace AS REGNAMESPACE) AS TEXT) = '{schema}' + ) AS t + GROUP BY + t.table_schema, + t.table_name + ) AS t4 + ON t1.table_schema = t4.table_schema AND t1.table_name = t4.table_name +) AS t +GROUP BY + t.table_schema diff --git a/plugins/dative/src/dative/core/data_source/metadata_sql/sqlite.sql b/plugins/dative/src/dative/core/data_source/metadata_sql/sqlite.sql new file mode 100644 index 000000000..130e4e901 --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/metadata_sql/sqlite.sql @@ -0,0 +1,66 @@ +SELECT + JSON_GROUP_ARRAY( + JSON_OBJECT( + 'name', m.name, + 'schema', 'main', + 'columns', JSON(t1.columns), + 'primary_keys', JSON(t2.primary_keys), + 'foreign_keys', COALESCE(JSON(t3.foreign_keys), JSON_ARRAY()) + ) + ) AS tables +FROM sqlite_master AS m +JOIN ( + SELECT + m.name, + JSON_GROUP_OBJECT( + p.name, + JSON_OBJECT( + 'name', p.name, + 'type', CASE + WHEN INSTR(UPPER(p.type), '(') > 0 + THEN SUBSTRING(UPPER(p.type), 1, INSTR(UPPER(p.type), '(') - 1) + ELSE UPPER(p.type) + END, + 'nullable', ( + CASE WHEN p."notnull" = 0 THEN TRUE ELSE FALSE END + ), + 'default', p.dflt_value + ) + ) AS columns + FROM sqlite_master AS m + JOIN PRAGMA_TABLE_INFO(m.name) AS p + ON m.type = 'table' + GROUP BY + m.name +) AS t1 + ON m.name = t1.name +LEFT JOIN ( + SELECT + m.name, + JSON_GROUP_ARRAY(p.name) AS primary_keys + FROM sqlite_master AS m + JOIN PRAGMA_TABLE_INFO(m.name) AS p + ON m.type = 'table' AND p.pk > 0 + GROUP BY + m.name +) AS t2 + ON m.name = t2.name +LEFT JOIN ( + SELECT + m.name, + JSON_GROUP_ARRAY( + JSON_OBJECT( + 'name', 'fk_' || m.tbl_name || '_' || fk."from" || '_' || fk."table" || '_' || fk."to", + 'column', fk."from", + 'referenced_schema', '', + 'referenced_table', fk."table", + 'referenced_column', fk."to" + ) + ) AS foreign_keys + FROM sqlite_master AS m + JOIN PRAGMA_FOREIGN_KEY_LIST(m.name) AS fk + ON m.type = 'table' + GROUP BY + m.tbl_name +) AS t3 + ON m.name = t3.name diff --git a/plugins/dative/src/dative/core/data_source/mysql_ds.py b/plugins/dative/src/dative/core/data_source/mysql_ds.py new file mode 100644 index 000000000..d7301041f --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/mysql_ds.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +import re +from pathlib import Path +from typing import Any, cast + +import orjson +from mysql.connector.aio import MySQLConnection, connect + +from .base import DatabaseMetadata, DataSourceBase, DBServerVersion, SQLError, SQLException + +with open(Path(__file__).parent / "metadata_sql" / "mysql.sql", encoding="utf-8") as f: + METADATA_SQL = f.read() + +version_p = re.compile(r"\b(v)?(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z0-9]+))?\b") + + +class Mysql(DataSourceBase): + def __init__(self, host: str, port: int, username: str, password: str, db_name: str): + self.host = host + self.port = port + self.username = username + self.password = password + self.db_name = db_name + + @property + def dialect(self) -> str: + return "mysql" + + @property + def string_types(self) -> set[str]: + return { + "CHAR", + "VARCHAR", + "TEXT", + "TINYTEXT", + "MEDIUMTEXT", + "LONGTEXT", + } + + @property + def json_array_agg_func(self) -> str: + return "JSON_ARRAYAGG" + + async def acreate_connection(self) -> MySQLConnection: + conn = await connect( + host=self.host, port=self.port, user=self.username, password=self.password, database=self.db_name + ) + return cast(MySQLConnection, conn) + + async def aget_server_version(self) -> DBServerVersion: + sql = "SELECT VERSION()" + _, rows, _ = await self.aexecute_raw_sql(sql) + version = cast(str, rows[0][0]) + match = version_p.match(version) + if match: + has_v, major, minor, patch, suffix = match.groups() + server_version = DBServerVersion(major=int(major), minor=int(minor)) + if patch: + server_version.patch = int(patch) + return server_version + else: + raise ValueError(f"Invalid version: {version}") + + async def aexecute_raw_sql(self, sql: str) -> tuple[list[str], list[tuple[Any, ...]], SQLException | None]: + if not sql: + return [], [], SQLException(error_type=SQLError.EmptySQL, msg="SQL语句不能为空") + + try: + conn = await self.acreate_connection() + except Exception as e: + return [], [], SQLException(error_type=SQLError.DBError, msg=e.args[1]) + + try: + cursor = await conn.cursor() + await cursor.execute(sql) + res = await cursor.fetchall() + if cursor.description: + cols = [i[0] for i in cursor.description] + else: + cols = [] + await cursor.close() + await conn.close() + return cols, res, None + except Exception as e: + return [], [], SQLException(error_type=SQLError.SyntaxError, msg=e.args[1], code=e.args[0]) + + async def aget_metadata(self) -> DatabaseMetadata: + sql = METADATA_SQL.format(db_name=self.db_name) + cols, rows, err = await self.aexecute_raw_sql(sql) + if err: + raise ConnectionError(err.msg) + if not rows or not rows[0][1]: + return DatabaseMetadata(name=self.db_name) + metadata = DatabaseMetadata.model_validate({ + "name": rows[0][0], + "tables": orjson.loads(cast(str, rows[0][1])), + }) + return metadata diff --git a/plugins/dative/src/dative/core/data_source/postgres_ds.py b/plugins/dative/src/dative/core/data_source/postgres_ds.py new file mode 100644 index 000000000..8b228a81d --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/postgres_ds.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +import re +from pathlib import Path +from typing import Any, cast + +import asyncpg +import orjson + +from .base import DatabaseMetadata, DataSourceBase, DBServerVersion, SQLError, SQLException + +with open(Path(__file__).parent / "metadata_sql" / "postgres.sql", encoding="utf-8") as f: + METADATA_SQL = f.read() + + +version_pattern = re.compile( + r".*(?:PostgreSQL|EnterpriseDB) " + r"(\d+)\.?(\d+)?(?:\.(\d+))?(?:\.\d+)?(?:devel|beta)?" +) + + +class Postgres(DataSourceBase): + """ + postgres不支持跨数据库查询 + """ + + def __init__(self, host: str, port: int, username: str, password: str, db_name: str, schema: str = "public"): + self.host = host + self.port = port + self.username = username + self.password = password + self.db_name = db_name + self.schema = schema + + @property + def dialect(self) -> str: + return "postgres" + + @property + def string_types(self) -> set[str]: + return {"CHARACTER VARYING", "VARCHAR", "CHAR", "CHARACTER", "TEXT"} + + @property + def json_array_agg_func(self) -> str: + return "JSON_AGG" + + async def acreate_connection(self) -> asyncpg.Connection: + conn = await asyncpg.connect( + host=self.host, + port=self.port, + user=self.username, + password=self.password, + database=self.db_name, + ) + return conn + + async def aget_server_version(self) -> DBServerVersion: + sql = "SELECT VERSION() as v" + _, rows, _ = await self.aexecute_raw_sql(sql) + version_str = cast(str, rows[0][0]) + m = version_pattern.match(version_str) + if not m: + raise AssertionError("Could not determine version from string '%s'" % version_str) + version = [int(x) for x in m.group(1, 2, 3) if x is not None] + server_version = DBServerVersion(major=version[0], minor=version[1]) + if len(version) > 2: + server_version.patch = version[2] + return server_version + + async def aexecute_raw_sql(self, sql: str) -> tuple[list[str], list[tuple[Any, ...]], SQLException | None]: + if not sql: + return [], [], SQLException(error_type=SQLError.EmptySQL, msg="SQL语句不能为空") + + try: + conn = await self.acreate_connection() + except Exception as e: + return [], [], SQLException(error_type=SQLError.DBError, msg=str(e)) + + try: + res: list[asyncpg.Record] = await conn.fetch(sql) + return list(res[0].keys()), [tuple(x.values()) for x in res], None + except Exception as e: + return [], [], SQLException(error_type=SQLError.SyntaxError, msg=str(e)) + + async def aget_metadata(self) -> DatabaseMetadata: + sql = METADATA_SQL.format(schema=self.schema) + cols, rows, err = await self.aexecute_raw_sql(sql) + if err: + raise ConnectionError(err.msg) + if not rows or not rows[0][1]: + return DatabaseMetadata(name=self.db_name) + metadata = DatabaseMetadata.model_validate({ + "name": self.db_name, + "tables": orjson.loads(cast(str, rows[0][1])), + }) + return metadata diff --git a/plugins/dative/src/dative/core/data_source/sqlite_ds.py b/plugins/dative/src/dative/core/data_source/sqlite_ds.py new file mode 100644 index 000000000..aaccdba1f --- /dev/null +++ b/plugins/dative/src/dative/core/data_source/sqlite_ds.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from pathlib import Path +from typing import Any, cast + +import aiosqlite +import orjson + +from .base import DatabaseMetadata, DataSourceBase, DBServerVersion, SQLError, SQLException + +with open(Path(__file__).parent / "metadata_sql" / "sqlite.sql", encoding="utf-8") as f: + METADATA_SQL = f.read() + + +class Sqlite(DataSourceBase): + def __init__(self, db_path: str | Path): + if isinstance(db_path, str): + db_path = Path(db_path) + self.db_path = db_path + self.db_name = db_path.stem + + @property + def dialect(self) -> str: + return "sqlite" + + @property + def string_types(self) -> set[str]: + return {"TEXT"} + + @property + def json_array_agg_func(self) -> str: + return "JSON_GROUP_ARRAY" + + async def conn_test(self) -> bool: + if self.db_path.is_file(): + return True + return False + + async def aget_server_version(self) -> DBServerVersion: + version = [int(i) for i in aiosqlite.sqlite_version.split(".") if i.isdigit()] + server_version = DBServerVersion(major=version[0], minor=version[1]) + if len(version) > 2: + server_version.patch = version[2] + return server_version + + async def aget_metadata(self) -> DatabaseMetadata: + _, rows, err = await self.aexecute_raw_sql(METADATA_SQL) + if err: + raise ConnectionError(err.msg) + if not rows or not rows[0][1]: + return DatabaseMetadata(name=self.db_name) + metadata = DatabaseMetadata.model_validate({ + "name": self.db_name, + "tables": orjson.loads(cast(str, rows[0][0])), + }) + return metadata + + async def aexecute_raw_sql(self, sql: str) -> tuple[list[str], list[tuple[Any, ...]], SQLException | None]: + try: + async with aiosqlite.connect(self.db_path) as db: + cursor = await db.execute(sql) + res = await cursor.fetchall() + return [i[0] for i in cursor.description], cast(list[tuple[Any, ...]], res), None + except Exception as e: + return [], [], SQLException(error_type=SQLError.SyntaxError, msg=str(e)) diff --git a/plugins/dative/src/dative/core/evaluation/__init__.py b/plugins/dative/src/dative/core/evaluation/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/plugins/dative/src/dative/core/evaluation/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/plugins/dative/src/dative/core/evaluation/schema_retrieval.py b/plugins/dative/src/dative/core/evaluation/schema_retrieval.py new file mode 100644 index 000000000..36838d7b7 --- /dev/null +++ b/plugins/dative/src/dative/core/evaluation/schema_retrieval.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from ..data_source.base import DBTable +from ..utils import JOIN_CHAR, calculate_metrics, parse_real_cols + + +def calculate_retrieval_metrics( + tables: list[DBTable], gold_sql: str, dialect="mysql", case_insensitive: bool = True +) -> tuple[float, float, float]: + gold_cols = parse_real_cols(gold_sql, dialect, case_insensitive) + recall_cols = set() + for table in tables: + for cn in table.columns.keys(): + col_name = f"{table.name}{JOIN_CHAR}{cn}" + if case_insensitive: + col_name = col_name.lower() + recall_cols.add(col_name) + + tp = len(gold_cols & recall_cols) + fp = len(recall_cols - gold_cols) + fn = len(gold_cols - recall_cols) + return calculate_metrics(tp, fp, fn) diff --git a/plugins/dative/src/dative/core/evaluation/sql_generation.py b/plugins/dative/src/dative/core/evaluation/sql_generation.py new file mode 100644 index 000000000..6252eae60 --- /dev/null +++ b/plugins/dative/src/dative/core/evaluation/sql_generation.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- + +from typing import Any + +from ..utils import calculate_metrics + + +def calculate_ex(predicted_res: list[tuple[Any, ...]], ground_truth_res: list[tuple[Any, ...]]) -> int: + res = 0 + if set(predicted_res) == set(ground_truth_res): + res = 1 + return res + + +def calculate_row_match( + predicted_row: tuple[Any, ...], ground_truth_row: tuple[Any, ...] +) -> tuple[float, float, float]: + """ + Calculate the matching percentage for a single row. + + Args: + predicted_row (tuple): The predicted row values. + ground_truth_row (tuple): The actual row values from ground truth. + + Returns: + float: The match percentage (0 to 1 scale). + """ + total_columns = len(ground_truth_row) + matches = 0 + element_in_pred_only = 0 + element_in_truth_only = 0 + for pred_val in predicted_row: + if pred_val in ground_truth_row: + matches += 1 + else: + element_in_pred_only += 1 + for truth_val in ground_truth_row: + if truth_val not in predicted_row: + element_in_truth_only += 1 + match_percentage = matches / total_columns + pred_only_percentage = element_in_pred_only / total_columns + truth_only_percentage = element_in_truth_only / total_columns + return match_percentage, pred_only_percentage, truth_only_percentage + + +def calculate_f1( + predicted: list[tuple[Any, ...]], ground_truth: list[tuple[Any, ...]] +) -> tuple[float, float, float]: + """ + Calculate the F1 score based on sets of predicted results and ground truth results, + where each element (tuple) represents a row from the database with multiple columns. + + Args: + predicted (set of tuples): Predicted results from SQL query. + ground_truth (set of tuples): Actual results expected (ground truth). + + Returns: + float: The calculated F1 score. + """ + # if both predicted and ground_truth are empty, return 1.0 for f1_score + if not predicted and not ground_truth: + return 1.0, 1.0, 1.0 + + # Calculate matching scores for each possible pair + match_scores: list[float] = [] + pred_only_scores: list[float] = [] + truth_only_scores: list[float] = [] + for i, gt_row in enumerate(ground_truth): + # rows only in the ground truth results + if i >= len(predicted): + match_scores.append(0) + truth_only_scores.append(1) + continue + pred_row = predicted[i] + match_score, pred_only_score, truth_only_score = calculate_row_match(pred_row, gt_row) + match_scores.append(match_score) + pred_only_scores.append(pred_only_score) + truth_only_scores.append(truth_only_score) + + # rows only in the predicted results + for i in range(len(predicted) - len(ground_truth)): + match_scores.append(0) + pred_only_scores.append(1) + truth_only_scores.append(0) + + tp = sum(match_scores) + fp = sum(pred_only_scores) + fn = sum(truth_only_scores) + + precision, recall, f1_score = calculate_metrics(tp, fp, fn) + return precision, recall, f1_score + +def calculate_ves( + predicted_res: list[tuple[Any, ...]], + ground_truth_res: list[tuple[Any, ...]], + pred_cost_time:float, + ground_truth_cost_time:float +): + time_ratio = 0 + if set(predicted_res) == set(ground_truth_res): + time_ratio = ground_truth_cost_time/pred_cost_time + + if time_ratio == 0: + reward = 0 + elif time_ratio >= 2: + reward = 1.25 + elif 1 <= time_ratio < 2: + reward = 1 + elif 0.5 <= time_ratio < 1: + reward = 0.75 + elif 0.25 <= time_ratio < 0.5: + reward = 0.5 + else: + reward = 0.25 + return reward diff --git a/plugins/dative/src/dative/core/sql_generation/__init__.py b/plugins/dative/src/dative/core/sql_generation/__init__.py new file mode 100644 index 000000000..3e4be4a27 --- /dev/null +++ b/plugins/dative/src/dative/core/sql_generation/__init__.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +import asyncio +from json import JSONDecodeError +from typing import cast + +from langchain_core.language_models import BaseChatModel +from langchain_core.messages import AIMessageChunk, HumanMessage + +from ..utils import cal_tokens, get_beijing_time, text2json + +prompt_prefix = """ +You are a helpful data analyst who is great at thinking deeply and reasoning about the user's question and the database schema. + +## 1. Database info +{db_info} + +## 2. Context Information +- Current Time: {current_time}. It is only used when calculation or conversion is required according to the current system time. + +## 3. Constraints +- Generate an optimized SQL query that directly answers the user's question. +- The SQL query must be fully formed, valid, and executable. +- Do NOT include any explanations, markdown formatting, or comments. +- If you want the maximum or minimum value, do not limit it to 1. +- When you need to calculate the proportion or other indicators, please use double precision. + +## 4. Evidence +{evidence} + +## 5. QUESTION +User's Question: {user_query} +""" # noqa: E501 + +generation_prompt = """ +Respond in the following JSON format: +- If the user query is not related to the database, answer with empty string. +{{ + "answer": "" +}} +- If you can answer the questions based on the database schema and don't need to generate SQL, generate the answers directly. +{{ + "answer": "answer based on database schema" +}} +- If you need to answer the question by querying the database, please generate SQL, select only the necessary fields needed to answer the question, without any missing or extra information. +- Prefer using aggregate functions (such as COUNT, SUM, etc.) in SQL query and avoid returning redundant data for post-processing. +{{ + "sql": "Generated SQL query here" +}} +""" # noqa: E501 + +correction_prompt = """ +There is a SQL, but an error was reported after execution. Analyze why the given SQL query does not produce the correct results, identify the issues, and provide a corrected SQL query that properly answers the user's request. + +- Current SQL Query: {current_sql} +- Error: {error} + +**What You Need to Do:** +1. **Analyze:** Explain why the current SQL query fails to produce the correct results. +2. **Provide a Corrected SQL Query:** Write a revised query that returns the correct results. + +Respond in the following JSON format: +{{ + "sql": "The corrected SQL query should be placed here." +}} +""" # noqa: E501 + + +async def arun( + query: str, + llm: BaseChatModel, + db_info: str, + evidence: str = "", + error_sql: str | None = None, + error_msg: str | None = None, +) -> tuple[str | None, str, int, int, str | None]: + answer = None + sql = "" + error = None + data = { + "db_info": db_info, + "user_query": query, + "current_time": await asyncio.to_thread(get_beijing_time), + "evidence": evidence or "", + } + input_tokens, output_tokens = 0, 0 + if not error_sql: + prompt = prompt_prefix + generation_prompt + else: + assert error_msg, "error_msg is required when you need to correct sql" + data["current_sql"] = error_sql + data["error"] = error_msg + prompt = prompt_prefix + correction_prompt + try: + content = "" + async for chunk in llm.astream([HumanMessage(prompt.format(**data))]): + msg = cast(AIMessageChunk, chunk) + content += cast(str, msg.content) + input_tokens, output_tokens = await asyncio.to_thread(cal_tokens, msg) + except Exception as e: + raise ValueError(f"调用LLM失败:{e}") + + if content.startswith("SELECT"): + sql = content + elif content.find("```sql") != -1: + sql = content.split("```sql")[1].split("```")[0] + else: + try: + result = await asyncio.to_thread(text2json, content) + if "answer" in result: + answer = result.get("answer") or "" + else: + sql = result.get("sql") or "" + except JSONDecodeError: + error = "Incorrect json format" + + return answer, sql, input_tokens, output_tokens, error diff --git a/plugins/dative/src/dative/core/sql_inspection/__init__.py b/plugins/dative/src/dative/core/sql_inspection/__init__.py new file mode 100644 index 000000000..6d36272db --- /dev/null +++ b/plugins/dative/src/dative/core/sql_inspection/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +from .check import SQLCheck +from .optimization import SQLOptimization + +__all__ = ["SQLCheck", "SQLOptimization"] diff --git a/plugins/dative/src/dative/core/sql_inspection/check.py b/plugins/dative/src/dative/core/sql_inspection/check.py new file mode 100644 index 000000000..3fdde8a48 --- /dev/null +++ b/plugins/dative/src/dative/core/sql_inspection/check.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from sqlglot import exp, parse_one + + +class SQLCheck: + def __init__(self, dialect: str): + self.dialect = dialect + + def is_query(self, sql_or_exp: str | exp.Expression) -> bool: + """判断是否是查询语句""" + if isinstance(sql_or_exp, exp.Expression): + expression = sql_or_exp + else: + expression = parse_one(sql_or_exp, dialect=self.dialect) + return isinstance(expression, exp.Query) + + def syntax_valid(self, sql: str) -> exp.Expression: + """基本语法验证""" + return parse_one(sql, dialect=self.dialect) diff --git a/plugins/dative/src/dative/core/sql_inspection/optimization.py b/plugins/dative/src/dative/core/sql_inspection/optimization.py new file mode 100644 index 000000000..11fc81c36 --- /dev/null +++ b/plugins/dative/src/dative/core/sql_inspection/optimization.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- + +from typing import Any, cast + +from sqlglot import ParseError, exp, parse_one +from sqlglot.optimizer.eliminate_subqueries import eliminate_subqueries +from sqlglot.optimizer.optimizer import RULES, optimize + + +class SQLOptimization: + def __init__(self, dialect: str, db_major_version: int): + self.dialect = dialect + self.db_major_version = db_major_version + + @staticmethod + def cte_to_subquery(expression: exp.Expression) -> exp.Expression: + # 收集所有 CTE + cte_names = {} + for cte in expression.find_all(exp.CTE): + alias = cte.alias + query = cte.this + cte_names[alias] = query + # 移除原始 CTE 节点 + if cte.parent: + cte.parent.pop() + + if not cte_names: + return expression + + # 替换所有对 CTE 的引用为子查询 + def replace_cte_with_subquery(node: exp.Expression) -> exp.Expression: + if isinstance(node, exp.Table) and node.name in cte_names: + subquery = cte_names[node.name] + return exp.Subquery(this=subquery.copy(), alias=node.alias or node.name) + + return node + + return expression.transform(replace_cte_with_subquery) + + @staticmethod + def optimize_in_limit_subquery(expression: exp.Expression) -> exp.Expression: + """ + Avoid 'LIMIT & IN/ALL/ANY/SOME subquery' + """ + for in_expr in expression.find_all(exp.In): + subquery = in_expr.args.get("query") + if not subquery: + continue + if subquery.this.find(exp.Limit): + t = subquery.this.args.get("from").this + if t.args.get("alias"): + alias = t.args.get("alias").this.this + else: + alias = exp.TableAlias(this=exp.Identifier(this="t")) + derived_table = exp.Subquery(this=subquery.this.copy(), alias=alias) + # 构建新的 SELECT t.id FROM (subquery) AS t + new_subquery_select = exp.select(*subquery.this.expressions).from_(derived_table) + # 替换 IN 的子查询 + in_expr.set("query", exp.Subquery(this=new_subquery_select)) + + return expression + + @staticmethod + def fix_missing_group_by_when_agg_func(expression: exp.Expression) -> exp.Expression: + """ + case2:SELECT a, COUNT(b) FROM x --> SELECT a, b FROM x GROUP BY a + case3:SELECT a FROM x ORDER BY MAX(b) --> SELECT a FROM x GROUP BY a ORDER BY MAX(b) + """ + for select_expr in expression.find_all(exp.Select): + select_agg = False + not_agg_query_cols = dict() + group_cols = dict() + order_by_agg = False + for col in select_expr.expressions: + if isinstance(col, exp.Column): + not_agg_query_cols[col.this.this] = col + elif isinstance(col, exp.AggFunc): + select_agg = True + elif isinstance(col, exp.Alias): + if isinstance(col.this, exp.Column): + not_agg_query_cols[col.this.this.this] = col.this + elif isinstance(col.this, exp.AggFunc): + select_agg = True + + if expression.args.get("group"): + for col in expression.args["group"].expressions: + group_cols[col.this.this] = col + if expression.args.get("order"): + for order_col in expression.args["order"].expressions: + if isinstance(order_col.this, exp.AggFunc): + order_by_agg = True + + if group_cols or (select_agg and not_agg_query_cols) or order_by_agg: + for col in not_agg_query_cols: + if col not in group_cols: + group_cols[col] = not_agg_query_cols[col] + + if group_cols: + select_expr.set("group", exp.Group(expressions=group_cols.values())) + + return expression + + @staticmethod + def set_limit(expression: exp.Expression, result_num_limit: int): + if expression.args.get("limit"): + limit_exp = cast(exp.Limit, expression.args.get("limit")) + limit = min(result_num_limit, int(limit_exp.expression.this)) + else: + limit = result_num_limit + expression.set("limit", exp.Limit(expression=exp.Literal.number(limit))) + + def arun( + self, + sql_or_exp: str | exp.Expression, + schema_type: dict[str, dict[str, Any]] | None = None, + result_num_limit: int | None = None, + ) -> str: + """ + Args: + sql_or_exp: + schema_type: db schema type, a mapping in one of the following forms: + 1. {table: {col: type}} + 2. {db: {table: {col: type}}} + 3. {catalog: {db: {table: {col: type}}}} + result_num_limit: int + Returns: str, optimized sql + """ + if isinstance(sql_or_exp, exp.Expression): + expression = sql_or_exp + else: + try: + expression = parse_one(sql_or_exp, dialect=self.dialect) + except ParseError: + expression = parse_one(sql_or_exp) + + if result_num_limit and result_num_limit > 0: + self.set_limit(expression, result_num_limit) + + rules = list(RULES) + if self.dialect == "mysql" and self.db_major_version < 8: + # mysql 8.0以上才支持 CTE,否则只能用子查询 + rules.remove(eliminate_subqueries) + rules.append(self.cte_to_subquery) + + # 优化 in limit 子查询 + rules.append(self.optimize_in_limit_subquery) + # 当聚合查询时,修复sql中 GROUP BY 缺少的字段 + rules.append(self.fix_missing_group_by_when_agg_func) + expression = optimize( + expression, + schema=schema_type, + dialect=self.dialect, + rules=rules, # type: ignore + identify=False, + ) + return expression.sql(self.dialect) diff --git a/plugins/dative/src/dative/core/sql_res_evaluation/__init__.py b/plugins/dative/src/dative/core/sql_res_evaluation/__init__.py new file mode 100644 index 000000000..56fa0eefd --- /dev/null +++ b/plugins/dative/src/dative/core/sql_res_evaluation/__init__.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +import asyncio +from json import JSONDecodeError +from typing import Any, cast + +from langchain_core.language_models import BaseChatModel +from langchain_core.messages import AIMessage, HumanMessage + +from ..utils import cal_tokens, text2json + +prompt = """ +# Database info +{db_info} + +# evidence: +{evidence} + +# Original user query: +{query} + +# Generated SQL: +{generated_sql} + +# SQL query results: +{sql_result} + +# Constraints: +- 1. Evaluate the relevance and quality of the SQL query results to the original user query. +- 2. If SQL results is empty or relevant, according to the above information directly answer the user's question, do not output "according to", "based on" and other redundant phrases. + Respond in the following JSON format: + {{ + "final_answer": "Return a full natural language answer" + }} +- 3. If SQL results is not empty and not relevant, explain of why the SQL query is not relevant. + Respond in the following JSON format: + {{ + "explanation": "explanation of why the SQL query is not relevant" + }} +""" # noqa:E501 + + +async def arun( + query: str, + llm: BaseChatModel, + db_info: str, + sql: str, + res_rows: list[tuple[Any, ...]], + evidence: str | None = None, +) -> tuple[str, int, int, str | None]: + answer = "" + error = None + input_tokens, output_tokens = 0, 0 + human_msg = prompt.format( + db_info=db_info, + query=query, + generated_sql=sql, + sql_result=res_rows, + evidence=evidence or "", + ) + try: + content = "" + async for chunk in llm.astream([HumanMessage(human_msg)]): + msg = cast(AIMessage, chunk) + content += cast(str, msg.content) + input_tokens, output_tokens = await asyncio.to_thread(cal_tokens, msg) + except Exception as e: + raise ValueError(f"调用LLM失败:{e}") + + try: + result = await asyncio.to_thread(text2json, cast(str, content)) + if "final_answer" in result: + answer = result["final_answer"] or "" + else: + error = result.get("explanation", "") + except JSONDecodeError: + error = "Incorrect json format" + + return answer, input_tokens, output_tokens, error diff --git a/plugins/dative/src/dative/core/utils.py b/plugins/dative/src/dative/core/utils.py new file mode 100644 index 000000000..1f4daa6b7 --- /dev/null +++ b/plugins/dative/src/dative/core/utils.py @@ -0,0 +1,247 @@ +# -*- coding: utf-8 -*- + +import asyncio +import datetime +import functools +import inspect +import os +import re +import uuid +from concurrent.futures import ThreadPoolExecutor +from decimal import Decimal +from typing import Any, Callable + +import json_repair as json +from dateutil.parser import parse as date_parse +from langchain_core.messages import AIMessage +from sqlglot import exp, parse_one +from sqlglot.optimizer.qualify import qualify +from sqlglot.optimizer.scope import Scope, traverse_scope + +JOIN_CHAR = "." + +email_re = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}") + + +def text2json(text: str) -> dict[Any, Any]: + res = json.loads(text) + if isinstance(res, dict): + return res + return dict() + + +def get_beijing_time(fmt: str = "%Y-%m-%d %H:%M:%S") -> str: + utc_now = datetime.datetime.now(datetime.UTC) + beijing_now = utc_now + datetime.timedelta(hours=8) + formatted_time = beijing_now.strftime(fmt) + return formatted_time + + +def cal_tokens(msg: AIMessage) -> tuple[int, int]: + if msg.usage_metadata: + return msg.usage_metadata["input_tokens"], msg.usage_metadata["output_tokens"] + elif msg.response_metadata.get("token_usage", {}).get("input_tokens"): + token_usage = msg.response_metadata.get("token_usage", {}) + input_tokens = token_usage.get("input_tokens", 0) + output_tokens = token_usage.get("output_tokens", 0) + return input_tokens, output_tokens + else: + return 0, 0 + + +def convert_value2str(value: Any) -> str: + if isinstance(value, str): + return value + elif isinstance(value, Decimal): + return str(float(value)) + elif value is None: + return "" + else: + return str(value) + + +def is_valid_uuid(s: str) -> bool: + try: + uuid.UUID(s) + return True + except ValueError: + return False + + +def is_number(value: Any) -> bool: + try: + float(value) + return True + except (ValueError, TypeError): + return False + + +def is_date(value: Any) -> bool: + try: + date_parse(value) + return True + except ValueError: + return False + + +def is_email(value: Any) -> bool: + if email_re.match(value): + return True + return False + + +def truncate_text(content: Any, *, max_length: int, suffix: str = "...") -> Any: + if not isinstance(content, str) or max_length <= 0: + return content + + if len(content) <= max_length: + return content + + if max_length <= len(suffix): + return content[:max_length] + + # 确保截断后的文本不会超过最大长度,且不会在单词中间截断。 + return content[: max_length - len(suffix)].rsplit(" ", 1)[0] + suffix + + +def truncate_text_by_byte(content: Any, *, max_length: int, suffix: str = "...", encoding: str = "utf-8") -> Any: + if not isinstance(content, str) or max_length <= 0: + return content + + encoded = content.encode(encoding) + if len(encoded) <= max_length: + return content + + suffix_bytes = suffix.encode(encoding) + available_bytes = max_length - len(suffix_bytes) + + if available_bytes <= 0: + for i in range(max_length, 0, -1): + try: + return encoded[:i].decode(encoding) + except UnicodeDecodeError: + continue + return "" + + # 尝试找到合适的截断位置,保证解码安全 + for i in range(available_bytes, 0, -1): + try: + truncated_part = encoded[:i].decode(encoding) + return truncated_part + suffix + except UnicodeDecodeError: + continue + + return suffix_bytes[:max_length].decode(encoding, errors="ignore") + + +async def async_parallel_exec(func: Callable[[Any], Any], data: list[Any], concurrency: int | None = None) -> list[Any]: + if not inspect.iscoroutinefunction(func): + async_func = functools.partial(asyncio.to_thread, func) # type: ignore + else: + async_func = func # type: ignore + + if len(data) == 0: + return [] + + if concurrency is None: + concurrency = len(data) + semaphore = asyncio.Semaphore(concurrency) + + async def worker(*args, **kwargs) -> Any: + async with semaphore: + return await async_func(*args, **kwargs) # type: ignore + + t_list = [] + for i in range(len(data)): + if isinstance(data[i], (list, tuple)): + t_list.append(worker(*data[i])) + elif isinstance(data[i], dict): + t_list.append(worker(**data[i])) + else: + t_list.append(worker(data[i])) + + return await asyncio.gather(*t_list) + + +def parallel_exec( + func: Callable[[Any], Any], data: list[dict[Any, Any] | tuple[Any] | list[Any]], concurrency: int | None = None +) -> list[Any]: + if len(data) == 0: + return [] + + t_list = [] + if concurrency is None: + concurrency = min(len(data), 32, (os.cpu_count() or 1) + 4) + with ThreadPoolExecutor(max_workers=concurrency) as pool: + for i in range(len(data)): + if isinstance(data[i], (list, tuple)): + t_list.append(pool.submit(func, *data[i])) + elif isinstance(data[i], dict): + t_list.append(pool.submit(func, **data[i])) # type: ignore + else: + t_list.append(pool.submit(func, data[i])) + + return [t.result() for t in t_list] + + +def exec_async_func(func: Callable[[Any], Any], *args, **kwargs) -> Any: + if inspect.iscoroutinefunction(func): + try: + loop = asyncio.get_event_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + return loop.run_until_complete(func(*args, **kwargs)) + else: + return func(*args, **kwargs) + + +def parse_col_name_from_scope( + scope: Scope, real_cols: set[str], cte_names: set[str], case_insensitive: bool = True +) -> None: + if case_insensitive: + scope.sources = {k.lower(): v for k, v in scope.sources.items()} + + for col in scope.columns: + if col.table: + tn = col.table + if case_insensitive: + tn = tn.lower() + if tn in scope.sources: + source = scope.sources[tn] + if isinstance(source, exp.Table) and tn not in cte_names: + src_tn = source.name + col_name = f"{src_tn}{JOIN_CHAR}{col.name}" + if case_insensitive: + col_name = col_name.lower() + real_cols.add(col_name) + elif not col.table and scope.tables: + tn = scope.tables[0].name + if tn not in cte_names: + col_name = f"{tn}{JOIN_CHAR}{col.name}" + if case_insensitive: + col_name = col_name.lower() + real_cols.add(col_name) + + +def parse_real_cols(sql: str, dialect="mysql", case_insensitive: bool = True) -> set[str]: + try: + parsed = parse_one(sql, dialect=dialect) + parsed = qualify(parsed, dialect=dialect) + cte_names = set() + for cte in parsed.find_all(exp.CTE): + cte_names.add(cte.alias) + scopes = traverse_scope(parsed) + real_cols: set[str] = set() + for scope in scopes: + parse_col_name_from_scope(scope, real_cols, cte_names, case_insensitive) + return real_cols + except Exception as e: + print(f"Error when parsing, error: {e}") + return set() + + +def calculate_metrics(tp: float, fp: float, fn: float) -> tuple[float, float, float]: + precision = tp / (tp + fp) if tp + fp > 0 else 0 + recall = tp / (tp + fn) if tp + fn > 0 else 0 + f1_score = 2 * precision * recall / (precision + recall) if precision + recall > 0 else 0 + return precision, recall, f1_score diff --git a/plugins/dative/src/dative/main.py b/plugins/dative/src/dative/main.py new file mode 100644 index 000000000..6d0a52154 --- /dev/null +++ b/plugins/dative/src/dative/main.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from dative.api import v1 + +app = FastAPI() + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) +app.include_router(v1.router, prefix="/api/v1") + + +@app.get("/healthz") +async def healthz() -> str: + return "ok" + + +if __name__ == "__main__": + import uvicorn + + uvicorn.run(app, host="0.0.0.0", port=3000) diff --git a/plugins/dative/tests/conftest.py b/plugins/dative/tests/conftest.py new file mode 100644 index 000000000..a522b22d4 --- /dev/null +++ b/plugins/dative/tests/conftest.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +import os +import sys +import time +from typing import Callable + +import pytest + +# 将 src 目录添加到 sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src"))) + +# 注: pytest.fixture 与 unittest.TestCase 类 混合使用不兼容 +if os.name != "nt": + + @pytest.fixture(scope="session", autouse=True) + def setup_and_teardown(): + # Setup logic + + yield # 运行测试 + + # Teardown logic + + +def wait_until(condition_function: Callable, timeout=10): + start_time = time.time() + while not condition_function(): + if timeout is not None and time.time() - start_time > timeout: + raise TimeoutError("Condition not met within the specified timeout") + time.sleep(0.01) # 短暂休眠以减少 CPU 使用 diff --git a/plugins/dative/tests/unit/core/evaluation/schema_retrieval.py b/plugins/dative/tests/unit/core/evaluation/schema_retrieval.py new file mode 100644 index 000000000..053da4a8e --- /dev/null +++ b/plugins/dative/tests/unit/core/evaluation/schema_retrieval.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- + +from unittest.mock import Mock + +import pytest + +from src.dative.core.evaluation.schema_retrieval import calculate_f1_score + + +def test_calculate_f1_score_perfect_match(): + """测试检索结果与gold SQL完全匹配的情况""" + # 创建模拟的DBTable对象 + table1 = Mock() + table1.name = "table1" + table1.columns = {"a": None, "b": None} # columns.keys()返回['a', 'b'] + + table2 = Mock() + table2.name = "table2" + table2.columns = {"c": None} + + retrieval_res = [table1, table2] + gold_sql = "SELECT table1.a, table1.b, table2.c FROM table1 JOIN table2" + + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql) + + # 完全匹配时,precision, recall, f1都应该是1.0 + assert precision == 1.0 + assert recall == 1.0 + assert f1 == 1.0 + + +def test_calculate_f1_score_partial_match(): + """测试检索结果与gold SQL部分匹配的情况""" + # 创建模拟的DBTable对象 + table1 = Mock() + table1.name = "table1" + table1.columns = {"a": None, "b": None} + + retrieval_res = [table1] # 只检索到table1的列 + gold_sql = "SELECT table1.a, table1.b, table2.c FROM table1 JOIN table2" # gold SQL需要table1和table2的列 + + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql) + + # 精确度应该是1.0 (检索到的都正确),召回率应该是2/3 (只找到2个正确的,共3个) + assert precision == 1.0 + assert recall == pytest.approx(2 / 3) + assert f1 == pytest.approx(2 * (1.0 * 2 / 3) / (1.0 + 2 / 3)) + + +def test_calculate_f1_score_no_match(): + """测试检索结果与gold SQL完全不匹配的情况""" + # 创建模拟的DBTable对象 + table1 = Mock() + table1.name = "table3" # 与gold SQL中的表名不同 + table1.columns = {"d": None, "e": None} + + retrieval_res = [table1] + gold_sql = "SELECT table1.a, table1.b, table2.c FROM table1 JOIN table2" + + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql) + + # 没有匹配时,precision, recall, f1都应该是0.0 + assert precision == 0.0 + assert recall == 0.0 + assert f1 == 0.0 + + +def test_calculate_f1_score_case_insensitive(): + """测试大小写不敏感的情况""" + table1 = Mock() + table1.name = "Table1" # 大小写与gold SQL不同 + table1.columns = {"A": None, "B": None} + + retrieval_res = [table1] + gold_sql = "SELECT table1.a, table1.b FROM table1" + + # 默认大小写不敏感 + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql) + + # 应该匹配,因为大小写不敏感 + assert precision == 1.0 + assert recall == 1.0 + assert f1 == 1.0 + + +def test_calculate_f1_score_case_sensitive(): + """测试大小写敏感的情况""" + table1 = Mock() + table1.name = "Table1" # 大小写与gold SQL不同 + table1.columns = {"A": None, "B": None} + + retrieval_res = [table1] + gold_sql = "SELECT table1.a, table1.b FROM table1" + + # 设置大小写敏感 + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql, case_insensitive=False) + + # 应该不匹配,因为大小写敏感 + assert precision == 0.0 + assert recall == 0.0 + assert f1 == 0.0 + + +def test_calculate_f1_score_empty_inputs(): + """测试空输入的情况""" + # 空的检索结果和简单的SQL + retrieval_res = [] + gold_sql = "SELECT a FROM table1" + + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql) + + # 检索结果为空时,precision和f1应该是0,recall也应该是0 + assert precision == 0.0 + assert recall == 0.0 + assert f1 == 0.0 + + # 空SQL的情况 + table1 = Mock() + table1.name = "table1" + table1.columns = {"a": None} + + retrieval_res = [table1] + gold_sql = "" + + precision, recall, f1 = calculate_f1_score(retrieval_res, gold_sql) + + # SQL解析失败时应该返回0分 + assert precision == 0.0 + assert recall == 0.0 + assert f1 == 0.0 diff --git a/plugins/dative/tests/unit/core/evaluation/sql_generation.py b/plugins/dative/tests/unit/core/evaluation/sql_generation.py new file mode 100644 index 000000000..28d3ce1c8 --- /dev/null +++ b/plugins/dative/tests/unit/core/evaluation/sql_generation.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- + +import pytest + +from src.dative.core.evaluation.sql_generation import calculate_ex, calculate_f1_score, calculate_row_match + + +class TestCalculateEx: + """测试 calculate_ex 函数""" + + def test_exact_match(self): + """测试完全匹配的情况""" + predicted = [(1, "A"), (2, "B")] + ground_truth = [(1, "A"), (2, "B")] + assert calculate_ex(predicted, ground_truth) == 1 + + def test_exact_match_different_order(self): + """测试顺序不同但内容相同的匹配""" + predicted = [(2, "B"), (1, "A")] + ground_truth = [(1, "A"), (2, "B")] + assert calculate_ex(predicted, ground_truth) == 1 + + def test_no_match(self): + """测试完全不匹配的情况""" + predicted = [(1, "A"), (2, "B")] + ground_truth = [(3, "C"), (4, "D")] + assert calculate_ex(predicted, ground_truth) == 0 + + def test_partial_match(self): + """测试部分匹配的情况""" + predicted = [(1, "A"), (2, "B")] + ground_truth = [(1, "A"), (3, "C")] + assert calculate_ex(predicted, ground_truth) == 0 + + def test_empty_inputs(self): + """测试空输入的情况""" + assert calculate_ex([], []) == 1 + assert calculate_ex([(1, "A")], []) == 0 + assert calculate_ex([], [(1, "A")]) == 0 + + +class TestCalculateRowMatch: + """测试 calculate_row_match 函数""" + + def test_perfect_match(self): + """测试行完全匹配的情况""" + predicted = (1, "A", 3.14) + ground_truth = (1, "A", 3.14) + match_pct, pred_only_pct, truth_only_pct = calculate_row_match(predicted, ground_truth) + assert match_pct == 1.0 + assert pred_only_pct == 0.0 + assert truth_only_pct == 0.0 + + def test_partial_match(self): + """测试行部分匹配的情况""" + predicted = (1, "B", 3.14) + ground_truth = (1, "A", 3.14) + match_pct, pred_only_pct, truth_only_pct = calculate_row_match(predicted, ground_truth) + # 2个值匹配(1和3.14),共3列 + assert match_pct == pytest.approx(2 / 3) + assert pred_only_pct == pytest.approx(1 / 3) + assert truth_only_pct == pytest.approx(1 / 3) + + def test_no_match(self): + """测试行完全不匹配的情况""" + predicted = (2, "B", 2.71) + ground_truth = (1, "A", 3.14) + match_pct, pred_only_pct, truth_only_pct = calculate_row_match(predicted, ground_truth) + assert match_pct == 0.0 + assert pred_only_pct == 1.0 + assert truth_only_pct == 1.0 + + def test_duplicate_values(self): + """测试包含重复值的情况""" + predicted = (1, 1, "A") + ground_truth = (1, "A", "A") + match_pct, pred_only_pct, truth_only_pct = calculate_row_match(predicted, ground_truth) + # 1和A都存在于ground_truth中 + assert match_pct == 1.0 + assert pred_only_pct == 0.0 + assert truth_only_pct == 0.0 + + +class TestCalculateF1Score: + """测试 calculate_f1_score 函数""" + + def test_perfect_match(self): + """测试完全匹配的情况""" + predicted = [(1, "A"), (2, "B")] + ground_truth = [(1, "A"), (2, "B")] + f1_score = calculate_f1_score(predicted, ground_truth) + assert f1_score == 1.0 + + def test_perfect_match_different_order(self): + """测试顺序不同但内容相同的匹配""" + predicted = [(2, "B"), (1, "A")] + ground_truth = [(1, "A"), (2, "B")] + f1_score = calculate_f1_score(predicted, ground_truth) + assert f1_score == 1.0 + + def test_empty_results(self): + """测试都为空的情况""" + f1_score = calculate_f1_score([], []) + assert f1_score == 1.0 + + def test_one_empty_result(self): + """测试一个为空的情况""" + predicted = [(1, "A")] + ground_truth = [] + f1_score = calculate_f1_score(predicted, ground_truth) + assert f1_score == 0.0 + + predicted = [] + ground_truth = [(1, "A")] + f1_score = calculate_f1_score(predicted, ground_truth) + assert f1_score == 0.0 + + def test_partial_match(self): + """测试部分匹配的情况""" + predicted = [(1, "A", 10), (3, "C", 30)] + ground_truth = [(1, "A", 10), (2, "B", 20)] + + # 第一行完全匹配(1.0),第二行部分匹配(部分值匹配) + f1_score = calculate_f1_score(predicted, ground_truth) + # 需要确保返回的是一个合理的f1分数(0-1之间) + assert 0.0 <= f1_score <= 1.0 + + def test_duplicate_rows(self): + """测试重复行的情况""" + predicted = [(1, "A"), (1, "A"), (2, "B")] + ground_truth = [(1, "A"), (2, "B"), (2, "B")] + f1_score = calculate_f1_score(predicted, ground_truth) + # 重复项应该被去除,结果应该与去重后相同 + assert 0.0 <= f1_score <= 1.0 + + def test_different_row_lengths(self): + """测试行长度不同的情况""" + predicted = [(1, "A", 10, "extra")] + ground_truth = [(1, "A", 10)] + # 这种情况可能产生意外结果,但函数应该能处理 + f1_score = calculate_f1_score(predicted, ground_truth) + assert 0.0 <= f1_score <= 1.0 diff --git a/plugins/dative/tests/unit/core/sql_inspection/check.py b/plugins/dative/tests/unit/core/sql_inspection/check.py new file mode 100644 index 000000000..8edae0765 --- /dev/null +++ b/plugins/dative/tests/unit/core/sql_inspection/check.py @@ -0,0 +1,49 @@ +import pytest +from sqlglot import exp + +from dative.core.sql_inspection.check import SQLCheck + + +class TestSQLCheck: + @pytest.fixture + def sql_check(self): + return SQLCheck(dialect="mysql") + + def test_is_query_with_select_statement(self, sql_check): + """测试SELECT语句是否被识别为查询""" + sql = "SELECT * FROM users" + assert sql_check.is_query(sql) is True + + def test_is_query_with_insert_statement(self, sql_check): + """测试INSERT语句是否不被识别为查询""" + sql = "INSERT INTO users (name) VALUES ('Alice')" + assert sql_check.is_query(sql) is False + + def test_is_query_with_exp_expression(self, sql_check): + """测试直接传入Expression对象的情况""" + expression = exp.select("*").from_("users") + assert sql_check.is_query(expression) is True + + def test_syntax_valid_with_correct_sql(self, sql_check): + """测试语法正确的SQL""" + sql = "SELECT id, name FROM users WHERE age > 18" + result = sql_check.syntax_valid(sql) + assert isinstance(result, exp.Expression) + + def test_syntax_valid_with_incorrect_sql(self, sql_check): + """测试语法错误的SQL应抛出异常""" + sql = "SELECT FROM WHERE" + with pytest.raises(Exception): + sql_check.syntax_valid(sql) + + def test_mysql_dialect(self): + """测试MySQL方言""" + sql_check = SQLCheck(dialect="mysql") + sql = "SELECT * FROM users LIMIT 10" + assert sql_check.is_query(sql) is True + + def test_postgres_dialect(self): + """测试PostgreSQL方言""" + sql_check = SQLCheck(dialect="postgres") + sql = "SELECT * FROM users LIMIT 10" + assert sql_check.is_query(sql) is True diff --git a/plugins/dative/tests/unit/core/sql_inspection/optimization.py b/plugins/dative/tests/unit/core/sql_inspection/optimization.py new file mode 100644 index 000000000..f3502d52d --- /dev/null +++ b/plugins/dative/tests/unit/core/sql_inspection/optimization.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- + +import pytest +from sqlglot import exp, parse_one + +from dative.core.sql_inspection.optimization import SQLOptimization + + +class TestSQLOptimization: + """SQLOptimization 类的单元测试""" + + @pytest.fixture + def mysql_optimizer(self): + """MySQL 5.7 版本的优化器""" + return SQLOptimization(dialect="mysql", db_major_version=5) + + @pytest.fixture + def mysql8_optimizer(self): + """MySQL 8.0 版本的优化器""" + return SQLOptimization(dialect="mysql", db_major_version=8) + + @pytest.fixture + def postgres_optimizer(self): + """PostgreSQL 优化器""" + return SQLOptimization(dialect="postgres", db_major_version=13) + + def test_cte_to_subquery(self): + """测试 CTE 转换为子查询的功能""" + sql = """ + WITH cte1 AS (SELECT id, name FROM users WHERE age > 18) + SELECT * \ + FROM cte1 \ + WHERE name LIKE 'A%' \ + """ + expression = parse_one(sql) + result = SQLOptimization.cte_to_subquery(expression) + + # 验证 CTE 已被替换为子查询 + assert "WITH" not in result.sql() + assert "users" in result.sql() + assert "age > 18" in result.sql() + + def test_optimize_in_limit_subquery(self): + """测试优化带 LIMIT 的 IN 子查询""" + sql = """ + SELECT * \ + FROM orders + WHERE user_id IN (SELECT id FROM users LIMIT 10) \ + """ + expression = parse_one(sql) + result = SQLOptimization.optimize_in_limit_subquery(expression) + + # 验证 LIMIT 子查询已被重写 + subquery = result.find(exp.In).args["query"] + assert subquery is not None + # 确保新的子查询结构正确 + assert isinstance(subquery, exp.Subquery) + + def test_fix_missing_group_by_when_agg_func_case1(self): + """测试修复 GROUP BY 缺失字段 - 情况1""" + sql = "SELECT a, b FROM x GROUP BY a" + expression = parse_one(sql) + result = SQLOptimization.fix_missing_group_by_when_agg_func(expression) + + # 应该添加缺失的 GROUP BY 字段 b + group_clause = result.args.get("group") + assert group_clause is not None + group_expressions = group_clause.expressions + assert len(group_expressions) == 2 + + def test_fix_missing_group_by_when_agg_func_case2(self): + """测试修复 GROUP BY 缺失字段 - 情况2""" + sql = "SELECT a, COUNT(b) FROM x" + expression = parse_one(sql) + result = SQLOptimization.fix_missing_group_by_when_agg_func(expression) + + # 应该自动添加 GROUP BY a + group_clause = result.args.get("group") + assert group_clause is not None + + def test_fix_missing_group_by_when_agg_func_case3(self): + """测试修复 GROUP BY 缺失字段 - 情况3""" + sql = "SELECT a FROM x ORDER BY MAX(b)" + expression = parse_one(sql) + result = SQLOptimization.fix_missing_group_by_when_agg_func(expression) + + # 应该添加 GROUP BY a + group_clause = result.args.get("group") + assert group_clause is not None + + @pytest.mark.asyncio + async def test_arun_with_string_sql(self, mysql_optimizer): + """测试 arun 方法处理字符串 SQL""" + sql = "SELECT id, name FROM users" + result = mysql_optimizer.arun(sql, result_num_limit=100) + + assert "LIMIT 100" in result + + @pytest.mark.asyncio + async def test_arun_with_expression(self, mysql_optimizer): + """测试 arun 方法处理表达式对象""" + expression = parse_one("SELECT id, name FROM users") + result = mysql_optimizer.arun(expression, result_num_limit=50) + + assert "LIMIT 50" in result + + @pytest.mark.asyncio + async def test_mysql_cte_handling_old_version(self, mysql_optimizer): + """测试 MySQL 低版本 CTE 处理(应转换为子查询)""" + sql = """ + WITH cte AS (SELECT id FROM users) + SELECT * + FROM cte \ + """ + result = mysql_optimizer.arun(sql) + + # 在 MySQL 5.x 中,CTE 应被转换为子查询 + assert "WITH" not in result + + @pytest.mark.asyncio + async def test_mysql_cte_handling_new_version(self, mysql8_optimizer): + """测试 MySQL 8.0+ CTE 处理(应保留 CTE)""" + sql = """ + WITH cte AS (SELECT id FROM users) + SELECT * + FROM cte + join b + on cte.id = b.id \ + """ + result = mysql8_optimizer.arun(sql) + # 在 MySQL 8.0+ 中,CTE 应被保留 + assert "WITH" in result + + @pytest.mark.asyncio + async def test_optimization_with_schema(self, postgres_optimizer): + """测试带模式信息的优化""" + sql = "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id" + schema = {"users": {"id": "INT", "name": "VARCHAR"}, "orders": {"id": "INT", "user_id": "INT"}} + result = postgres_optimizer.arun(sql, schema_type=schema) + + assert result is not None + assert isinstance(result, str) diff --git a/plugins/dative/tests/unit/core/utils.py b/plugins/dative/tests/unit/core/utils.py new file mode 100644 index 000000000..2179833c6 --- /dev/null +++ b/plugins/dative/tests/unit/core/utils.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- +import asyncio +import datetime +import uuid +from decimal import Decimal +from unittest.mock import MagicMock + +import pytest + +from dative.core.utils import ( + async_parallel_exec, + cal_tokens, + calculate_f1, + exec_async_func, + get_beijing_time, + is_date, + is_email, + is_number, + is_valid_uuid, + parallel_exec, + parse_real_cols, + text2json, + truncate_text, + truncate_text_by_byte, +) +from src.dative.core.utils import convert_value2str + + +def test_text2json_valid_json(): + text = '{"name": "Alice", "age": 30}' + expected = {"name": "Alice", "age": 30} + assert text2json(text) == expected + + +def test_text2json_invalid_json(): + text = "invalid json" + expected = {} + assert text2json(text) == expected + + +def test_text2json_non_dict_result(): + text = '["not", "a", "dict"]' + expected = {} + assert text2json(text) == expected + + +def test_case_001_usage_metadata_exists(): + msg = MagicMock() + msg.usage_metadata = {"input_tokens": 10, "output_tokens": 20} + msg.response_metadata = {} + result = cal_tokens(msg) + + assert result == (10, 20) + + +def test_case_002_token_usage_in_response_metadata(): + msg = MagicMock() + msg.usage_metadata = None + msg.response_metadata = {"token_usage": {"input_tokens": 5, "output_tokens": 15}} + result = cal_tokens(msg) + assert result == (5, 15) + + +def test_get_beijing_time_default_format(): + result = get_beijing_time() + assert datetime.datetime.strptime(result, "%Y-%m-%d %H:%M:%S") + + +def test_get_beijing_time_custom_format(): + fmt = "%Y/%m/%d" + result = get_beijing_time(fmt) + assert datetime.datetime.strptime(result, fmt) + + +def test_convert_value2str_string(): + assert convert_value2str("hello") == "hello" + + +def test_convert_value2str_decimal(): + assert convert_value2str(Decimal("10.5")) == "10.5" + + +def test_convert_value2str_none(): + assert convert_value2str(None) == "" + + +def test_convert_value2str_other_types(): + assert convert_value2str(123) == "123" + assert convert_value2str(45.67) == "45.67" + assert convert_value2str(True) == "True" + + +def test_is_valid_uuid(): + valid_uuid = str(uuid.uuid4()) + assert is_valid_uuid(valid_uuid) is True + assert is_valid_uuid("invalid-uuid") is False + + +def test_is_number(): + assert is_number("123") is True + assert is_number("123.45") is True + assert is_number(123) is True + assert is_number("not_a_number") is False + assert is_number(None) is False + + +def test_is_date(): + assert is_date("2023-01-01") is True + assert is_date("Jan 1, 2023") is True + assert is_date("invalid-date") is False + + +def test_is_email(): + assert is_email("test@example.com") is True + assert is_email("invalid-email") is False + + +def test_truncate_text(): + text = "This is a long text for testing" + assert truncate_text(text, max_length=10) == "This..." + assert truncate_text(text, max_length=100) == text + assert truncate_text(text, max_length=3) == "Thi" + assert truncate_text(123, max_length=10) == 123 + + +def test_truncate_text_by_byte(): + text = "这是一个用于测试的长文本" + result = truncate_text_by_byte(text, max_length=20) + assert isinstance(result, str) + assert len(result.encode("utf-8")) <= 20 + + # 测试英文文本 + english_text = "This is English text" + result = truncate_text_by_byte(english_text, max_length=15) + assert len(result.encode("utf-8")) <= 15 + + +def sample_function(x): + return x * 2 + + +async def async_sample_function(x): + await asyncio.sleep(0.01) # 模拟异步操作 + return x * 2 + + +def test_parallel_exec(): + data = [1, 2, 3, 4, 5] + result = parallel_exec(sample_function, data) + assert result == [2, 4, 6, 8, 10] + + +def test_parallel_exec_with_kwargs(): + def func_with_kwargs(a, b): + return a + b + + data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}] + result = parallel_exec(func_with_kwargs, data) + assert result == [3, 7] + + +@pytest.mark.asyncio +async def test_async_parallel_exec(): + data = [1, 2, 3, 4, 5] + result = await async_parallel_exec(async_sample_function, data) + assert result == [2, 4, 6, 8, 10] + + +@pytest.mark.asyncio +async def test_async_parallel_exec_with_sync_function(): + data = [1, 2, 3, 4, 5] + result = await async_parallel_exec(sample_function, data) + assert result == [2, 4, 6, 8, 10] + + +async def async_add(a, b): + await asyncio.sleep(0.1) # 模拟异步操作 + return a + b + + +def test_exec_async_func(): + result = exec_async_func(async_add, 1, 2) + assert result == 3 + + +def test_exec_async_func_with_kwargs(): + result = exec_async_func(async_add, a=1, b=2) + assert result == 3 + + +def test_exec_async_func_with_mixed_args(): + result = exec_async_func(async_add, 1, b=2) + assert result == 3 + + +def test_calculate_f1_normal_case(): + """测试正常情况下的F1计算""" + precision, recall, f1_score = calculate_f1(10, 5, 3) + assert precision == 10 / 15 # 0.6667 + assert recall == 10 / 13 # 0.7692 + expected_f1 = 2 * precision * recall / (precision + recall) + assert f1_score == expected_f1 + + +def test_calculate_f1_zero_precision_and_recall(): + """测试精确度和召回率都为0的情况""" + precision, recall, f1_score = calculate_f1(0, 0, 0) + assert precision == 0 + assert recall == 0 + assert f1_score == 0 + + +def test_calculate_f1_zero_precision(): + """测试精确度为0的情况""" + precision, recall, f1_score = calculate_f1(0, 5, 5) + assert precision == 0 + assert recall == 0 + assert f1_score == 0 + + +def test_calculate_f1_zero_recall(): + """测试召回率为0的情况""" + precision, recall, f1_score = calculate_f1(0, 0, 5) + assert precision == 0 + assert recall == 0 + assert f1_score == 0 + + +def test_parse_real_cols_simple_select(): + """测试简单SELECT查询""" + sql = "SELECT a, b FROM table1" + result = parse_real_cols(sql) + expected = {"table1.a", "table1.b"} + assert result == expected + + +def test_parse_real_cols_with_join(): + """测试包含JOIN的查询""" + sql = "SELECT t1.a, t2.b FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id" + result = parse_real_cols(sql) + expected = {"table1.a", "table1.id", "table2.b", "table2.id"} + assert result == expected + + +def test_parse_real_cols_with_cte(): + """测试包含CTE的查询""" + sql = """ + WITH cte1 AS (SELECT a FROM table1) + SELECT cte1.a, table2.b + FROM cte1 + JOIN table2 ON cte1.a = table2.a \ + """ + result = parse_real_cols(sql) + expected = {"table1.a", "table2.a", "table2.b"} + assert result == expected + + +def test_parse_real_cols_case_insensitive(): + """测试大小写不敏感情况""" + sql = "SELECT A, b FROM Table1" + result = parse_real_cols(sql, case_insensitive=True) + expected = {"table1.a", "table1.b"} + assert result == expected + + +def test_parse_real_cols_case_sensitive(): + """测试大小写敏感情况""" + sql = "SELECT A, b FROM Table1" + result = parse_real_cols(sql, case_insensitive=False) + expected = {"Table1.A", "Table1.b"} + assert result == expected diff --git a/plugins/dative/uv.lock b/plugins/dative/uv.lock new file mode 100644 index 000000000..e446ed96f --- /dev/null +++ b/plugins/dative/uv.lock @@ -0,0 +1,2032 @@ +version = 1 +requires-python = "==3.11.*" + +[[package]] +name = "aiobotocore" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aioitertools" }, + { name = "botocore" }, + { name = "jmespath" }, + { name = "multidict" }, + { name = "python-dateutil" }, + { name = "wrapt" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/05/93/9f5243c2fd2fc22cff92f8d8a7e98d3080171be60778d49aeabb555a463d/aiobotocore-2.24.2.tar.gz", hash = "sha256:dfb21bdb2610e8de4d22f401e91a24d50f1330a302d03c62c485757becd439a9" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/87/03/2330062ac4ea9fa6447e02b0625f24efd6f05b6c44d61d86610b3555ee66/aiobotocore-2.24.2-py3-none-any.whl", hash = "sha256:808c63b2bd344b91e2f2acb874831118a9f53342d248acd16a68455a226e283a" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8" }, +] + +[[package]] +name = "aiohttp" +version = "3.12.15" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117" }, + { url = "https://mirrors.aliyun.com/pypi/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe" }, + { url = "https://mirrors.aliyun.com/pypi/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728" }, + { url = "https://mirrors.aliyun.com/pypi/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16" }, + { url = "https://mirrors.aliyun.com/pypi/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50" }, + { url = "https://mirrors.aliyun.com/pypi/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685" }, + { url = "https://mirrors.aliyun.com/pypi/packages/36/ab/1006278d1ffd13a698e5dd4bfa01e5878f6bddefc296c8b62649753ff249/aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/10/97/ad2b18700708452400278039272032170246a1bf8ec5d832772372c71f1a/aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d" }, +] + +[[package]] +name = "aioitertools" +version = "0.12.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/de/38491a84ab323b47c7f86e94d2830e748780525f7a10c8600b67ead7e9ea/aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/85/13/58b70a580de00893223d61de8fea167877a3aed97d4a5e1405c9159ef925/aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e" }, +] + +[[package]] +name = "aiosqlite" +version = "0.21.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" }, +] + +[[package]] +name = "anyio" +version = "4.10.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1" }, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/4c/0e/f5d708add0d0b97446c402db7e8dd4c4183c13edaabe8a8500b411e7b495/asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6a/a0/67ec9a75cb24a1d99f97b8437c8d56da40e6f6bd23b04e2f4ea5d5ad82ac/asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5c/d9/a7584f24174bd86ff1053b14bb841f9e714380c672f61c906eb01d8ec433/asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a0/d7/a4c0f9660e333114bdb04d1a9ac70db690dd4ae003f34f691139a5cbdae3/asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3c/21/199fd16b5a981b1575923cbb5d9cf916fdc936b377e0423099f209e7e73d/asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056" }, + { url = "https://mirrors.aliyun.com/pypi/packages/77/52/0004809b3427534a0c9139c08c87b515f1c77a8376a50ae29f001e53962f/asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454" }, + { url = "https://mirrors.aliyun.com/pypi/packages/52/cb/fbad941cd466117be58b774a3f1cc9ecc659af625f028b163b1e646a55fe/asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3c/0a/0a32307cf166d50e1ad120d9b81a33a948a1a5463ebfa5a96cc5606c0863/asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f" }, +] + +[[package]] +name = "asyncpg-stubs" +version = "0.30.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "asyncpg" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a3/e5/1a06ecec2a77a75974ba6b22d3bed697193177c0ed7595cce4dd2362735d/asyncpg_stubs-0.30.2.tar.gz", hash = "sha256:b8a1b7cb790a7b8a0e4e64e438a97c3fac77ea02441b563b1975748f18af33ab" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a4/22/77a4a08cc9ef4f8bbb5e7ffbf4be008e596b535a3533a28c3465e9400d75/asyncpg_stubs-0.30.2-py3-none-any.whl", hash = "sha256:e57818bbaf10945a60ff3219da3c5ce97e1b424503b6a6f0a18db99797397cbb" }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3" }, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34" }, +] + +[[package]] +name = "boto3-stubs" +version = "1.40.25" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "botocore-stubs" }, + { name = "types-s3transfer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/11/ad/da879e0bacca97dfc2bcf1fd190f83c5a85f5729da13ef0ec8fbb6070360/boto3_stubs-1.40.25.tar.gz", hash = "sha256:420aedc1dc5ae653977af711c7b925f95645bcfdff294c6d0fabaac994182e09" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/fe/f7/39ba74ec964abf8f5fdf09ba79d9b309f8fbeca6fa1638c0ee790a3464f3/boto3_stubs-1.40.25-py3-none-any.whl", hash = "sha256:6fbc7f4775b03cd4856d408fa7bd5d3b0bb0326f0aa561ffdcd33cca564eb729" }, +] + +[[package]] +name = "botocore" +version = "1.40.18" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6a/91/2e745382793fa7d30810a7d5ca3e05f6817b6db07601ca5aaab12720caf9/botocore-1.40.18.tar.gz", hash = "sha256:afd69bdadd8c55cc89d69de0799829e555193a352d87867f746e19020271cc0f" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/1a/f5/bd57bf21fdcc4e500cc406ed2c296e626ddd160f0fee2a4932256e5d62d8/botocore-1.40.18-py3-none-any.whl", hash = "sha256:57025c46ca00cf8cec25de07a759521bfbfb3036a0f69b272654a354615dc45f" }, +] + +[[package]] +name = "botocore-stubs" +version = "1.40.25" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "types-awscrt" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fe/6b/c38b34da65a93a299ec5aef97c8faeb4812d0d18f41303b3a1a919bbfe80/botocore_stubs-1.40.25.tar.gz", hash = "sha256:47e5308a2bc566ff69c0506830370af99fd29504b5077c2dbe31da0ff32cc32d" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/c3/64/1e58877fd43ffafba3daafa64b2b6032be09f0ed0412c112cf2de2cc964f/botocore_stubs-1.40.25-py3-none-any.whl", hash = "sha256:ddd3f7d8e50ad227ca9337c2ac2049c4269908766a4a5d1273a5b5d727c38fc3" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5" }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf" }, + { url = "https://mirrors.aliyun.com/pypi/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" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07" }, + { url = "https://mirrors.aliyun.com/pypi/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14" }, + { url = "https://mirrors.aliyun.com/pypi/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae" }, + { url = "https://mirrors.aliyun.com/pypi/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, +] + +[[package]] +name = "coverage" +version = "7.10.6" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62" }, + { url = "https://mirrors.aliyun.com/pypi/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153" }, + { url = "https://mirrors.aliyun.com/pypi/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619" }, + { url = "https://mirrors.aliyun.com/pypi/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "45.0.7" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339" }, + { url = "https://mirrors.aliyun.com/pypi/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513" }, + { url = "https://mirrors.aliyun.com/pypi/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443" }, + { url = "https://mirrors.aliyun.com/pypi/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691" }, + { url = "https://mirrors.aliyun.com/pypi/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17" }, + { url = "https://mirrors.aliyun.com/pypi/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90" }, + { url = "https://mirrors.aliyun.com/pypi/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde" }, + { url = "https://mirrors.aliyun.com/pypi/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae" }, + { url = "https://mirrors.aliyun.com/pypi/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63" }, +] + +[[package]] +name = "dative" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "aiobotocore" }, + { name = "aiosqlite" }, + { name = "asyncpg" }, + { name = "cryptography" }, + { name = "duckdb" }, + { name = "fastapi" }, + { name = "fastexcel" }, + { name = "greenlet" }, + { name = "httpx", extra = ["socks"] }, + { name = "json-repair" }, + { name = "langchain-core" }, + { name = "langchain-openai" }, + { name = "mysql-connector-python" }, + { name = "orjson" }, + { name = "pyarrow" }, + { name = "pydantic-settings" }, + { name = "python-dateutil" }, + { name = "python-dotenv" }, + { name = "pytz" }, + { name = "sqlglot", extra = ["rs"] }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.dev-dependencies] +dev = [ + { name = "asyncpg-stubs" }, + { name = "boto3-stubs" }, + { name = "fastapi-cli" }, + { name = "hatch" }, + { name = "mypy" }, + { name = "pyarrow-stubs" }, + { name = "pytest" }, + { name = "pytest-async" }, + { name = "pytest-asyncio" }, + { name = "pytest-benchmark" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "ruff" }, + { name = "types-aiobotocore", extra = ["essential"] }, + { name = "types-python-dateutil" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiobotocore", specifier = ">=2.24.2" }, + { name = "aiosqlite", specifier = ">=0.21.0" }, + { name = "asyncpg", specifier = ">=0.30.0" }, + { name = "cryptography", specifier = ">=45.0.6" }, + { name = "duckdb", specifier = ">=1.3.2" }, + { name = "fastapi", specifier = "<1.0" }, + { name = "fastexcel", specifier = ">=0.15.1" }, + { name = "greenlet", specifier = ">=3.2.4" }, + { name = "httpx", extras = ["socks"], specifier = ">=0.28.1" }, + { name = "json-repair", specifier = ">=0.50.1" }, + { name = "langchain-core", specifier = "<1.0" }, + { name = "langchain-openai", specifier = "<1.0" }, + { name = "mysql-connector-python", specifier = ">=9.4.0" }, + { name = "orjson", specifier = ">=3.11.2" }, + { name = "pyarrow", specifier = ">=21.0.0" }, + { name = "pydantic-settings", specifier = ">=2.10.1" }, + { name = "python-dateutil", specifier = ">=2.9.0.post0" }, + { name = "python-dotenv", specifier = ">=1.1.1" }, + { name = "pytz", specifier = ">=2025.2" }, + { name = "sqlglot", extras = ["rs"], specifier = "==27.12.0" }, + { name = "uvicorn", extras = ["standard"], specifier = "<1.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "asyncpg-stubs", specifier = ">=0.30.2" }, + { name = "boto3-stubs", specifier = ">=1.40.25" }, + { name = "fastapi-cli", specifier = ">=0.0.8" }, + { name = "hatch", specifier = ">=1.14.1" }, + { name = "mypy", specifier = ">=1.17.1" }, + { name = "pyarrow-stubs", specifier = ">=20.0.0.20250825" }, + { name = "pytest", specifier = ">=8.3.3" }, + { name = "pytest-async", specifier = ">=0.1.1" }, + { name = "pytest-asyncio", specifier = ">=0.26.0" }, + { name = "pytest-benchmark", specifier = ">=4.0.0" }, + { name = "pytest-cov", specifier = ">=6.3.0" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "ruff", specifier = ">=0.12.10" }, + { name = "types-aiobotocore", extras = ["essential"], specifier = ">=2.24.2" }, + { name = "types-python-dateutil", specifier = ">=2.9.0.20250822" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2" }, +] + +[[package]] +name = "duckdb" +version = "1.3.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/47/24/a2e7fb78fba577641c286fe33185789ab1e1569ccdf4d142e005995991d2/duckdb-1.3.2.tar.gz", hash = "sha256:c658df8a1bc78704f702ad0d954d82a1edd4518d7a04f00027ec53e40f591ff5" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/38/16/4cde40c37dd1f48d2f9ffa63027e8b668391c5cc32cbb59f7ca8b1cec6e2/duckdb-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e1872cf63aae28c3f1dc2e19b5e23940339fc39fb3425a06196c5d00a8d01040" }, + { url = "https://mirrors.aliyun.com/pypi/packages/22/ca/9ca65db51868604007114a27cc7d44864d89328ad6a934668626618147ff/duckdb-1.3.2-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:db256c206056468ae6a9e931776bdf7debaffc58e19a0ff4fa9e7e1e82d38b3b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/9e/ca/7f7cf01dd7731d358632fb516521f2962070a627558fb6fc3137e594bbaa/duckdb-1.3.2-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1d57df2149d6e4e0bd5198689316c5e2ceec7f6ac0a9ec11bc2b216502a57b34" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4c/7f/38e518b8f51299410dcad9f1e99f1c99f3592516581467a2da344d3b5951/duckdb-1.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f76c8b1e2a19dfe194027894209ce9ddb073fd9db69af729a524d2860e4680" }, + { url = "https://mirrors.aliyun.com/pypi/packages/90/a3/41f3d42fddd9629846aac328eb295170e76782d8dfc5e58b3584b96fa296/duckdb-1.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45bea70b3e93c6bf766ce2f80fc3876efa94c4ee4de72036417a7bd1e32142fe" }, + { url = "https://mirrors.aliyun.com/pypi/packages/11/8e/c5444b6890ae7f00836fd0cd17799abbcc3066bbab32e90b04aa8a8a5087/duckdb-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:003f7d36f0d8a430cb0e00521f18b7d5ee49ec98aaa541914c6d0e008c306f1a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/87/a1/e240bd07671542ddf2084962e68a7d5c9b068d8da3f938e935af69441355/duckdb-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:0eb210cedf08b067fa90c666339688f1c874844a54708562282bc54b0189aac6" }, +] + +[[package]] +name = "fastapi" +version = "0.116.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565" }, +] + +[[package]] +name = "fastapi-cli" +version = "0.0.10" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "rich-toolkit" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/31/b6/ed25b8874a27f684bf601990c48fcb3edb478edca2b9a38cc2ba196fb304/fastapi_cli-0.0.10.tar.gz", hash = "sha256:85a93df72ff834c3d2a356164512cabaf8f093d50eddad9309065a9c9ac5193a" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/7c/62/0f00036925c0614e333a2baf739c861453a6779331ffb47ec9a6147f860b/fastapi_cli-0.0.10-py3-none-any.whl", hash = "sha256:04bef56b49f7357c6c4acd4f793b4433ed3f511be431ed0af68db6d3f8bd44b3" }, +] + +[[package]] +name = "fastexcel" +version = "0.15.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/40/2b/78e3dc13d71a532935662a8d531fe7aa9a2b6bdbd5eec4f1dce01904e6a7/fastexcel-0.15.1.tar.gz", hash = "sha256:bce738c82c4d043704fbcff802823c51f830ed75e8da64962859d2060d6c743b" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a8/4f/a39a0093cd7480e63ae88b7c3e310ddc3e3a59c6360191583ccedf305979/fastexcel-0.15.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d839bbb8326b37e6d167ecef522a1f20b14e0a7f18f9d1bba9530e12decf8628" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4d/bb/3690a11d077e101c6b2e2fb76d90464fa0af4ccdac04ce8638ab433f0a7d/fastexcel-0.15.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f94bc44026f261c0b1d212dcdfc315838b2943ea8176c5fdac389f30201eb7b6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/90/bd/736fce45b9dc58d3e3ffb9c27dca55e44f500491e4c73da7b4cf3c51995e/fastexcel-0.15.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7707bee43eb2e9f01cd051e613748505a893d866c95505eb036d920f3dc93f21" }, + { url = "https://mirrors.aliyun.com/pypi/packages/61/4b/9be7f0cf1be1436506a7e9389b4e0bdaec6348d6e3cc597f1387ccc94303/fastexcel-0.15.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6e598848e98408800574a120dd5b7f02ecbf3490b430829161240202f923140" }, + { url = "https://mirrors.aliyun.com/pypi/packages/30/4b/423fdbcd7475f373fad5f002acaf1bddf3fb8e915675e23cb58807ac11a1/fastexcel-0.15.1-cp39-abi3-win_amd64.whl", hash = "sha256:e002ad806cb07867a28787db2c0b4f62c91826219edf92a68b86cb7b177badef" }, +] + +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d" }, +] + +[[package]] +name = "frozenlist" +version = "1.7.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750" }, + { url = "https://mirrors.aliyun.com/pypi/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98" }, + { url = "https://mirrors.aliyun.com/pypi/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae" }, + { url = "https://mirrors.aliyun.com/pypi/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56" }, + { url = "https://mirrors.aliyun.com/pypi/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e" }, +] + +[[package]] +name = "greenlet" +version = "3.2.4" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633" }, + { url = "https://mirrors.aliyun.com/pypi/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" }, +] + +[[package]] +name = "hatch" +version = "1.14.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "click" }, + { name = "hatchling" }, + { name = "httpx" }, + { name = "hyperlink" }, + { name = "keyring" }, + { name = "packaging" }, + { name = "pexpect" }, + { name = "platformdirs" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "tomli-w" }, + { name = "tomlkit" }, + { name = "userpath" }, + { name = "uv" }, + { name = "virtualenv" }, + { name = "zstandard" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1f/43/c0b37db0e857a44ce5ffdb7e8a9b8fa6425d0b74dea698fafcd9bddb50d1/hatch-1.14.1.tar.gz", hash = "sha256:ca1aff788f8596b0dd1f8f8dfe776443d2724a86b1976fabaf087406ba3d0713" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a5/40/19c0935bf9f25808541a0e3144ac459de696c5b6b6d4511a98d456c69604/hatch-1.14.1-py3-none-any.whl", hash = "sha256:39cdaa59e47ce0c5505d88a951f4324a9c5aafa17e4a877e2fde79b36ab66c21" }, +] + +[[package]] +name = "hatchling" +version = "1.27.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55" }, +] + +[[package]] +name = "httptools" +version = "0.6.4" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" }, +] + +[package.optional-dependencies] +socks = [ + { name = "socksio" }, +] + +[[package]] +name = "hyperlink" +version = "21.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3a/51/1947bd81d75af87e3bb9e34593a4cf118115a8feb451ce7a69044ef1412e/hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6e/aa/8caf6a0a3e62863cbb9dab27135660acba46903b703e224f14f447e57934/hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" }, +] + +[[package]] +name = "jaraco-context" +version = "6.0.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "backports-tarfile" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.3.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683" }, +] + +[[package]] +name = "jiter" +version = "0.10.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978" }, + { url = "https://mirrors.aliyun.com/pypi/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc" }, + { url = "https://mirrors.aliyun.com/pypi/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db" }, + { url = "https://mirrors.aliyun.com/pypi/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606" }, + { url = "https://mirrors.aliyun.com/pypi/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812" }, +] + +[[package]] +name = "jmespath" +version = "1.0.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980" }, +] + +[[package]] +name = "json-repair" +version = "0.50.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/91/71/6d57ed93e43e98cdd124e82ab6231c6817f06a10743e7ae4bc6f66d03a02/json_repair-0.50.1.tar.gz", hash = "sha256:4ee69bc4be7330fbb90a3f19e890852c5fe1ceacec5ed1d2c25cdeeebdfaec76" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ad/be/b1e05740d9c6f333dab67910f3894e2e2416c1ef00f9f7e20a327ab1f396/json_repair-0.50.1-py3-none-any.whl", hash = "sha256:9b78358bb7572a6e0b8effe7a8bd8cb959a3e311144842b1d2363fe39e2f13c5" }, +] + +[[package]] +name = "jsonpatch" +version = "1.33" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "jsonpointer" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade" }, +] + +[[package]] +name = "jsonpointer" +version = "3.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942" }, +] + +[[package]] +name = "keyring" +version = "25.6.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd" }, +] + +[[package]] +name = "langchain-core" +version = "0.3.75" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "jsonpatch" }, + { name = "langsmith" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "tenacity" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/63/270b71a23e849984505ddc7c5c9fd3f4bd9cb14b1a484ee44c4e51c33cc2/langchain_core-0.3.75.tar.gz", hash = "sha256:ab0eb95a06ed6043f76162e6086b45037690cb70b7f090bd83b5ebb8a05b70ed" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/fb/42/0d0221cce6f168f644d7d96cb6c87c4e42fc55d2941da7a36e970e3ab8ab/langchain_core-0.3.75-py3-none-any.whl", hash = "sha256:03ca1fadf955ee3c7d5806a841f4b3a37b816acea5e61a7e6ba1298c05eea7f5" }, +] + +[[package]] +name = "langchain-openai" +version = "0.3.32" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "openai" }, + { name = "tiktoken" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7e/19/167d9ad1b6bb75406c4acceda01ef0dc1101c7f629f74441fe8a787fb190/langchain_openai-0.3.32.tar.gz", hash = "sha256:782ad669bd1bdb964456d8882c5178717adcfceecb482cc20005f770e43d346d" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e6/3d/e22ee65fff79afe7bdfbd67844243eb279b440c882dad9e4262dcc87131f/langchain_openai-0.3.32-py3-none-any.whl", hash = "sha256:3354f76822f7cc76d8069831fe2a77f9bc7ff3b4f13af788bd94e4c6e853b400" }, +] + +[[package]] +name = "langsmith" +version = "0.4.25" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "httpx" }, + { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "zstandard" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0c/84/2f01d51a557d14a9a5590c32de5dde3b466b00a55521450c2c9e77ffa438/langsmith-0.4.25.tar.gz", hash = "sha256:56f0c45810384fba37582ca17fdbcf6ead51934d26d72672e5a810452c0d4ae3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a5/85/8a5ca8f6044bd74acd0d364878b459d84ec460cf40aec17ed9cd5716e908/langsmith-0.4.25-py3-none-any.whl", hash = "sha256:adb61784ff58e65f0290ba45770626219fb06a776e69fbcf98aec580478b4686" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8" }, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b" }, +] + +[[package]] +name = "multidict" +version = "6.6.4" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6b/7f/90a7f01e2d005d6653c689039977f6856718c75c5579445effb7e60923d1/multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/54/a3/bed07bc9e2bb302ce752f1dabc69e884cd6a676da44fb0e501b246031fdd/multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a7/4b/ceeb4f8f33cf81277da464307afeaf164fb0297947642585884f5cad4f28/multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/03/35/436a5da8702b06866189b69f655ffdb8f70796252a8772a77815f1812679/multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b6/0e/915160be8fecf1fca35f790c08fb74ca684d752fcba62c11daaf3d92c216/multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683" }, + { url = "https://mirrors.aliyun.com/pypi/packages/08/ee/2f464330acd83f77dcc346f0b1a0eaae10230291450887f96b204b8ac4d3/multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/71/cc/9a117f828b4d7fbaec6adeed2204f211e9caf0a012692a1ee32169f846ae/multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/25/77/62752d3dbd70e27fdd68e86626c1ae6bccfebe2bb1f84ae226363e112f5a/multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50" }, + { url = "https://mirrors.aliyun.com/pypi/packages/00/6e/fac58b1072a6fc59af5e7acb245e8754d3e1f97f4f808a6559951f72a0d4/multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52" }, + { url = "https://mirrors.aliyun.com/pypi/packages/01/ef/4698d6842ef5e797c6db7744b0081e36fb5de3d00002cc4c58071097fac3/multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/aa/c9/d82e95ae1d6e4ef396934e9b0e942dfc428775f9554acf04393cce66b157/multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/57/cf/f94af5c36baaa75d44fab9f02e2a6bcfa0cd90acb44d4976a80960759dbc/multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4a/fe/29f23460c3d995f6a4b678cb2e9730e7277231b981f0b234702f0177818a/multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/29/b6/fd59449204426187b82bf8a75f629310f68c6adc9559dc922d5abe34797b/multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/19/52/d5d6b344f176a5ac3606f7a61fb44dc746e04550e1a13834dff722b8d7d6/multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ec/d3/5b2281ed89ff4d5318d82478a2a2450fcdfc3300da48ff15c1778280ad26/multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/74/7d/36b045c23a1ab98507aefd44fd8b264ee1dd5e5010543c6fccf82141ccef/multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/0f/5e/553d67d24432c5cd52b49047f2d248821843743ee6d29a704594f656d182/multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c" }, +] + +[[package]] +name = "mypy" +version = "1.17.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505" }, +] + +[[package]] +name = "mysql-connector-python" +version = "9.4.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/02/77/2b45e6460d05b1f1b7a4c8eb79a50440b4417971973bb78c9ef6cad630a6/mysql_connector_python-9.4.0.tar.gz", hash = "sha256:d111360332ae78933daf3d48ff497b70739aa292ab0017791a33e826234e743b" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/fe/0c/4365a802129be9fa63885533c38be019f1c6b6f5bcf8844ac53902314028/mysql_connector_python-9.4.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7df1a8ddd182dd8adc914f6dc902a986787bf9599705c29aca7b2ce84e79d361" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c0/bf/ca596c00d7a6eaaf8ef2f66c9b23cd312527f483073c43ffac7843049cb4/mysql_connector_python-9.4.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3892f20472e13e63b1fb4983f454771dd29f211b09724e69a9750e299542f2f8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/25/14/6510a11ed9f80d77f743dc207773092c4ab78d5efa454b39b48480315d85/mysql_connector_python-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d3e87142103d71c4df647ece30f98e85e826652272ed1c74822b56f6acdc38e7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/16/a8/4f99d80f1cf77733ce9a44b6adb7f0dd7079e7afa51ca4826515ef0c3e16/mysql_connector_python-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b27fcd403436fe83bafb2fe7fcb785891e821e639275c4ad3b3bd1e25f533206" }, + { url = "https://mirrors.aliyun.com/pypi/packages/15/9c/127f974ca9d5ee25373cb5433da06bb1f36e05f2a6b7436da1fe9c6346b0/mysql_connector_python-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd6ff5afb9c324b0bbeae958c93156cce4168c743bf130faf224d52818d1f0ee" }, + { url = "https://mirrors.aliyun.com/pypi/packages/36/34/b6165e15fd45a8deb00932d8e7d823de7650270873b4044c4db6688e1d8f/mysql_connector_python-9.4.0-py2.py3-none-any.whl", hash = "sha256:56e679169c704dab279b176fab2a9ee32d2c632a866c0f7cd48a8a1e2cf802c4" }, +] + +[[package]] +name = "openai" +version = "1.106.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/79/b6/1aff7d6b8e9f0c3ac26bfbb57b9861a6711d5d60bd7dd5f7eebbf80509b7/openai-1.106.1.tar.gz", hash = "sha256:5f575967e3a05555825c43829cdcd50be6e49ab6a3e5262f0937a3f791f917f1" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/00/e1/47887212baa7bc0532880d33d5eafbdb46fcc4b53789b903282a74a85b5b/openai-1.106.1-py3-none-any.whl", hash = "sha256:bfdef37c949f80396c59f2c17e0eda35414979bc07ef3379596a93c9ed044f3a" }, +] + +[[package]] +name = "orjson" +version = "3.11.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/be/4d/8df5f83256a809c22c4d6792ce8d43bb503be0fb7a8e4da9025754b09658/orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/cd/8b/360674cd817faef32e49276187922a946468579fcaf37afdfb6c07046e92/orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/05/3d/5fa9ea4b34c1a13be7d9046ba98d06e6feb1d8853718992954ab59d16625/orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e5/5f/e18367823925e00b1feec867ff5f040055892fc474bf5f7875649ecfa586/orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904" }, + { url = "https://mirrors.aliyun.com/pypi/packages/0f/bd/3c66b91c4564759cf9f473251ac1650e446c7ba92a7c0f9f56ed54f9f0e6/orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/82/b5/dc8dcd609db4766e2967a85f63296c59d4722b39503e5b0bf7fd340d387f/orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/48/c2/d58ec5fd1270b2aa44c862171891adc2e1241bd7dab26c8f46eb97c6c6f1/orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038" }, + { url = "https://mirrors.aliyun.com/pypi/packages/73/87/0ef7e22eb8dd1ef940bfe3b9e441db519e692d62ed1aae365406a16d23d0/orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bb/6a/e5bf7b70883f374710ad74faf99bacfc4b5b5a7797c1d5e130350e0e28a3/orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bd/0c/4577fd860b6386ffaa56440e792af01c7882b56d2766f55384b5b0e9d39b/orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55" }, + { url = "https://mirrors.aliyun.com/pypi/packages/66/4b/83e92b2d67e86d1c33f2ea9411742a714a26de63641b082bdbf3d8e481af/orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6d/e5/9eea6a14e9b5ceb4a271a1fd2e1dec5f2f686755c0fab6673dc6ff3433f4/orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824" }, + { url = "https://mirrors.aliyun.com/pypi/packages/45/78/8d4f5ad0c80ba9bf8ac4d0fc71f93a7d0dc0844989e645e2074af376c307/orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/0b/5f/16386970370178d7a9b438517ea3d704efcf163d286422bae3b37b88dbb5/orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204" }, + { url = "https://mirrors.aliyun.com/pypi/packages/09/60/db16c6f7a41dd8ac9fb651f66701ff2aeb499ad9ebc15853a26c7c152448/orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3e/2a/bb811ad336667041dea9b8565c7c9faf2f59b47eb5ab680315eea612ef2e/orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" }, +] + +[[package]] +name = "propcache" +version = "0.3.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95" }, + { url = "https://mirrors.aliyun.com/pypi/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39" }, + { url = "https://mirrors.aliyun.com/pypi/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35" }, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5" }, +] + +[[package]] +name = "pyarrow" +version = "21.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/94/dc/80564a3071a57c20b7c32575e4a0120e8a330ef487c319b122942d665960/pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ea/cc/3b51cb2db26fe535d14f74cab4c79b191ed9a8cd4cbba45e2379b5ca2746/pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10" }, + { url = "https://mirrors.aliyun.com/pypi/packages/24/11/a4431f36d5ad7d83b87146f515c063e4d07ef0b7240876ddb885e6b44f2e/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/74/dc/035d54638fc5d2971cbf1e987ccd45f1091c83bcf747281cf6cc25e72c88/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2e/3b/89fced102448a9e3e0d4dded1f37fa3ce4700f02cdb8665457fcc8015f5b/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fb/bb/ea7f1bd08978d39debd3b23611c293f64a642557e8141c80635d501e6d53/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6e/0b/77ea0600009842b30ceebc3337639a7380cd946061b620ac1a2f3cb541e2/pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6" }, +] + +[[package]] +name = "pyarrow-stubs" +version = "20.0.0.20250825" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "pyarrow" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/03/2c/2807ba3808971a8870686304a727908f84903be8ede36a3a399a0f36a13d/pyarrow_stubs-20.0.0.20250825.tar.gz", hash = "sha256:e128e575c00a978c851d7fb2f45bf793c3e4dda5c084cfb9e20cf839829c97d9" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/aa/5f/6233b7072f3b635dd29a42cc7d1c9fee8460bf86d4089a88cbf2e1c3580f/pyarrow_stubs-20.0.0.20250825-py3-none-any.whl", hash = "sha256:f6a5242c7874f89fb5c2d8f611dca2ec1125622b53067994a42fa64193ab8d29" }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30" }, + { url = "https://mirrors.aliyun.com/pypi/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab" }, + { url = "https://mirrors.aliyun.com/pypi/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65" }, + { url = "https://mirrors.aliyun.com/pypi/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593" }, + { url = "https://mirrors.aliyun.com/pypi/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.10.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79" }, +] + +[[package]] +name = "pytest-async" +version = "0.1.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ff/b6/c15344fd2894fe93f15ce0f503d25afc2506ebe01a3c3ea52044169d510c/pytest_async-0.1.1.tar.gz", hash = "sha256:0d6ffd3ebac2f3aa47d606dbae1984750268a89dc8caf4a908ba61c60299cdfd" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/89/4f/bdd43dd0838c2b0c0487a8de0480a30fb797e49abc4c8c68aca2912fd636/pytest_async-0.1.1-py3-none-any.whl", hash = "sha256:11cc41eef82592951d56c2bb9b0e6ab21b2f0f00663e78d95694a80d965be930" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.1.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf" }, +] + +[[package]] +name = "pytest-benchmark" +version = "5.1.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "py-cpuinfo" }, + { name = "pytest" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/39/d0/a8bd08d641b393db3be3819b03e2d9bb8760ca8479080a26a5f6e540e99c/pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/9e/d6/b41653199ea09d5969d4e385df9bbfd9a100f28ca7e824ce7c0a016e3053/pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89" }, +] + +[[package]] +name = "pytest-cov" +version = "6.3.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/30/4c/f883ab8f0daad69f47efdf95f55a66b51a8b939c430dadce0611508d9e99/pytest_cov-6.3.0.tar.gz", hash = "sha256:35c580e7800f87ce892e687461166e1ac2bcb8fb9e13aea79032518d6e503ff2" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/80/b4/bb7263e12aade3842b938bc5c6958cae79c5ee18992f9b9349019579da0f/pytest_cov-6.3.0-py3-none-any.whl", hash = "sha256:440db28156d2468cafc0415b4f8e50856a0d11faefa38f30906048fe490f1749" }, +] + +[[package]] +name = "pytest-mock" +version = "3.15.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/61/99/3323ee5c16b3637b4d941c362182d3e749c11e400bea31018c42219f3a98/pytest_mock-3.15.0.tar.gz", hash = "sha256:ab896bd190316b9d5d87b277569dfcdf718b2d049a2ccff5f7aca279c002a1cf" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/2b/b3/7fefc43fb706380144bcd293cc6e446e6f637ddfa8b83f48d1734156b529/pytest_mock-3.15.0-py3-none-any.whl", hash = "sha256:ef2219485fb1bd256b00e7ad7466ce26729b30eadfc7cbcdb4fa9a92ca68db6f" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317" }, + { url = "https://mirrors.aliyun.com/pypi/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85" }, + { url = "https://mirrors.aliyun.com/pypi/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44" }, +] + +[[package]] +name = "regex" +version = "2025.9.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b2/5a/4c63457fbcaf19d138d72b2e9b39405954f98c0349b31c601bfcb151582c/regex-2025.9.1.tar.gz", hash = "sha256:88ac07b38d20b54d79e704e38aa3bd2c0f8027432164226bdee201a1c0c9c9ff" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/06/4d/f741543c0c59f96c6625bc6c11fea1da2e378b7d293ffff6f318edc0ce14/regex-2025.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e5bcf112b09bfd3646e4db6bf2e598534a17d502b0c01ea6550ba4eca780c5e6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c2/bd/27e73e92635b6fbd51afc26a414a3133243c662949cd1cda677fe7bb09bd/regex-2025.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:67a0295a3c31d675a9ee0238d20238ff10a9a2fdb7a1323c798fc7029578b15c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/eb/7d/7dc0c6efc8bc93cd6e9b947581f5fde8a5dbaa0af7c4ec818c5729fdc807/regex-2025.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea8267fbadc7d4bd7c1301a50e85c2ff0de293ff9452a1a9f8d82c6cafe38179" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d1/01/9b5c6dd394f97c8f2c12f6e8f96879c9ac27292a718903faf2e27a0c09f6/regex-2025.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aeff21de7214d15e928fb5ce757f9495214367ba62875100d4c18d293750cc1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fc/24/b7430cfc6ee34bbb3db6ff933beb5e7692e5cc81e8f6f4da63d353566fb0/regex-2025.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d89f1bbbbbc0885e1c230f7770d5e98f4f00b0ee85688c871d10df8b184a6323" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d6/98/155f914b4ea6ae012663188545c4f5216c11926d09b817127639d618b003/regex-2025.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca3affe8ddea498ba9d294ab05f5f2d3b5ad5d515bc0d4a9016dd592a03afe52" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8a/a7/a470e7bc8259c40429afb6d6a517b40c03f2f3e455c44a01abc483a1c512/regex-2025.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91892a7a9f0a980e4c2c85dd19bc14de2b219a3a8867c4b5664b9f972dcc0c78" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1d/fa/33f6fec4d41449fea5f62fdf5e46d668a1c046730a7f4ed9f478331a8e3a/regex-2025.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e1cb40406f4ae862710615f9f636c1e030fd6e6abe0e0f65f6a695a2721440c6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/42/de/2b45f36ab20da14eedddf5009d370625bc5942d9953fa7e5037a32d66843/regex-2025.9.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:94f6cff6f7e2149c7e6499a6ecd4695379eeda8ccbccb9726e8149f2fe382e92" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1e/f9/878f4fc92c87e125e27aed0f8ee0d1eced9b541f404b048f66f79914475a/regex-2025.9.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6c0226fb322b82709e78c49cc33484206647f8a39954d7e9de1567f5399becd0" }, + { url = "https://mirrors.aliyun.com/pypi/packages/90/c2/5b6f2bce6ece5f8427c718c085eca0de4bbb4db59f54db77aa6557aef3e9/regex-2025.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a12f59c7c380b4fcf7516e9cbb126f95b7a9518902bcf4a852423ff1dcd03e6a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/47/66/1ef1081c831c5b611f6f55f6302166cfa1bc9574017410ba5595353f846a/regex-2025.9.1-cp311-cp311-win32.whl", hash = "sha256:49865e78d147a7a4f143064488da5d549be6bfc3f2579e5044cac61f5c92edd4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ad/e0/8adc550d7169df1d6b9be8ff6019cda5291054a0107760c2f30788b6195f/regex-2025.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:d34b901f6f2f02ef60f4ad3855d3a02378c65b094efc4b80388a3aeb700a5de7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/cb/bd/46fef29341396d955066e55384fb93b0be7d64693842bf4a9a398db6e555/regex-2025.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:47d7c2dab7e0b95b95fd580087b6ae196039d62306a592fa4e162e49004b6299" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6" }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06" }, +] + +[[package]] +name = "rich" +version = "14.1.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f" }, +] + +[[package]] +name = "rich-toolkit" +version = "0.15.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/67/33/1a18839aaa8feef7983590c05c22c9c09d245ada6017d118325bbfcc7651/rich_toolkit-0.15.1.tar.gz", hash = "sha256:6f9630eb29f3843d19d48c3bd5706a086d36d62016687f9d0efa027ddc2dd08a" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/c8/49/42821d55ead7b5a87c8d121edf323cb393d8579f63e933002ade900b784f/rich_toolkit-0.15.1-py3-none-any.whl", hash = "sha256:36a0b1d9a135d26776e4b78f1d5c2655da6e0ef432380b5c6b523c8d8ab97478" }, +] + +[[package]] +name = "ruff" +version = "0.12.12" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577" }, + { url = "https://mirrors.aliyun.com/pypi/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92" }, + { url = "https://mirrors.aliyun.com/pypi/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23" }, + { url = "https://mirrors.aliyun.com/pypi/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489" }, + { url = "https://mirrors.aliyun.com/pypi/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093" }, +] + +[[package]] +name = "secretstorage" +version = "3.3.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "jeepney" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" }, +] + +[[package]] +name = "socksio" +version = "1.0.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3" }, +] + +[[package]] +name = "sqlglot" +version = "27.12.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2c/8b/a19c3d9d6933f8ee6ea05a1df6e8b7ce48fd910bbb366ac9fbf522dcaa38/sqlglot-27.12.0.tar.gz", hash = "sha256:1bb0500503eea375bf86ddc72b2e9ca955113bd0cbf8968bcf4ed5f4cd8d5575" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e4/89/9dc71793f4cfbebbe9529986f887c1a627ffc57550f5de246409a5f721d4/sqlglot-27.12.0-py3-none-any.whl", hash = "sha256:b3a3d9d0cc27d7eece4057ff97714fe2d950ae9c5dc0df702db6fcd333565bb8" }, +] + +[package.optional-dependencies] +rs = [ + { name = "sqlglotrs" }, +] + +[[package]] +name = "sqlglotrs" +version = "0.6.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/11/de/0d42a6a0f8ee3129beb45f5b8e367667a43597adf459f2192f5d2346e379/sqlglotrs-0.6.2.tar.gz", hash = "sha256:7ed668215bdcea6f69dc9a29c9ea26ed39216ab330f357289a5ec95138c40482" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/fd/8b/3144a291b330f7b515cee288bc7ce399f7283bdd63fa8675d3994d7e4f1a/sqlglotrs-0.6.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7ca2fb09c3399ca1834a7180c9c6e3b5eb411d14cab5ac32d3c44b7ae5a1864b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1e/69/888f02e1ce625e3060f410afd42ef9287257f0b3618132512eccc9019023/sqlglotrs-0.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a03124b03e0cb7df6a61461114b4ba9d70f70f710f056bf004324e8533b98eb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b8/93/b67ca7a98dce3f618ce175f2f949de5670a7cda2246d49fedd75cf1d7631/sqlglotrs-0.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f3c84f2324ca7caf012143122780ed604cf9357cec3a633b6cdd67d250e049f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c6/80/237da36a77e52585673491d7948643b100a0f6f9b8ad8c40ddd5c7913886/sqlglotrs-0.6.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfad829eefb7ca348af471af97e531dcc780549daac517e30e95ff2f9675bc3c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4d/5b/24552c19f8551859574cd9fb246bb468d2c2ba2fdbf682659c7e196607c5/sqlglotrs-0.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:418f1de961e09fb6da5359746920faa0b0e71f0c912786a566f001e6419cff4c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/56/9e/ac43826d8ab359c70610b8fa29ccdbbdf6fcd44c91c93f6e278dcdca464b/sqlglotrs-0.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f100affd9f5c8450077621e53dfac67e54a95d654e2f36f304043c25ba73120" }, + { url = "https://mirrors.aliyun.com/pypi/packages/40/14/cf9fb69f3cf0bead5b5ee6cf8e81f52606d06afa853e3fef581a11469c59/sqlglotrs-0.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15275016fefc9e8af9f632b4f65681665f044015a203d8239573eaee646efe50" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fe/97/57c0c78068be144563a5c3cbea3fd7408e659a505bb637c776355b80a096/sqlglotrs-0.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:112415e5154828d7b5961eafb2df5091bd30a9a5185fe6bdc2170dd5a0a87eba" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e8/37/112bfd88175e102a54cce3bb8159fa92cbc5dee02f8f6004be207ac262a4/sqlglotrs-0.6.2-cp311-cp311-win32.whl", hash = "sha256:cad0b8ad679fb6026733f6ab70cfdadded25d5843d10b49d07b71a286d266308" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f4/4f/746867761232886932858b24752c25bafc1f98e53242cb00016c81aeb63f/sqlglotrs-0.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b11fadf56ebcaa2c346b40fe24e7428046c794edf361bda176e0dbb0aef39743" }, +] + +[[package]] +name = "starlette" +version = "0.47.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51" }, +] + +[[package]] +name = "tenacity" +version = "9.1.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138" }, +] + +[[package]] +name = "tiktoken" +version = "0.11.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a7/86/ad0155a37c4f310935d5ac0b1ccf9bdb635dcb906e0a9a26b616dd55825a/tiktoken-0.11.0.tar.gz", hash = "sha256:3c518641aee1c52247c2b97e74d8d07d780092af79d5911a6ab5e79359d9b06a" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/8a/91/912b459799a025d2842566fe1e902f7f50d54a1ce8a0f236ab36b5bd5846/tiktoken-0.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4ae374c46afadad0f501046db3da1b36cd4dfbfa52af23c998773682446097cf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8c/e9/6faa6870489ce64f5f75dcf91512bf35af5864583aee8fcb0dcb593121f5/tiktoken-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25a512ff25dc6c85b58f5dd4f3d8c674dc05f96b02d66cdacf628d26a4e4866b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a1/3e/a05d1547cf7db9dc75d1461cfa7b556a3b48e0516ec29dfc81d984a145f6/tiktoken-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2130127471e293d385179c1f3f9cd445070c0772be73cdafb7cec9a3684c0458" }, + { url = "https://mirrors.aliyun.com/pypi/packages/34/9a/db7a86b829e05a01fd4daa492086f708e0a8b53952e1dbc9d380d2b03677/tiktoken-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e43022bf2c33f733ea9b54f6a3f6b4354b909f5a73388fb1b9347ca54a069c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/9d/bb/52edc8e078cf062ed749248f1454e9e5cfd09979baadb830b3940e522015/tiktoken-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:adb4e308eb64380dc70fa30493e21c93475eaa11669dea313b6bbf8210bfd013" }, + { url = "https://mirrors.aliyun.com/pypi/packages/60/d9/884b6cd7ae2570ecdcaffa02b528522b18fef1cbbfdbcaa73799807d0d3b/tiktoken-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:ece6b76bfeeb61a125c44bbefdfccc279b5288e6007fbedc0d32bfec602df2f2" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee" }, + { url = "https://mirrors.aliyun.com/pypi/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" }, + { url = "https://mirrors.aliyun.com/pypi/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106" }, + { url = "https://mirrors.aliyun.com/pypi/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90" }, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0" }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2" }, +] + +[[package]] +name = "trove-classifiers" +version = "2025.8.26.11" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f7/7c/78ea329adc8be4353f9ef8ee5b7498450fcbd1a02fed6cd444344eb0bf63/trove_classifiers-2025.8.26.11.tar.gz", hash = "sha256:e73efff317c492a7990092f9c12676c705bf6cfe40a258a93f63f4b4c9941432" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/4a/40/d54944eeb5646fb4b1c98d4601fe5e0812dd2e7c0aa94d53fc46457effc8/trove_classifiers-2025.8.26.11-py3-none-any.whl", hash = "sha256:887fb0a402bdbecd4415a52c06e6728f8bdaa506a7143372d2b893e2c5e2d859" }, +] + +[[package]] +name = "typer" +version = "0.17.4" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/92/e8/2a73ccf9874ec4c7638f172efc8972ceab13a0e3480b389d6ed822f7a822/typer-0.17.4.tar.gz", hash = "sha256:b77dc07d849312fd2bb5e7f20a7af8985c7ec360c45b051ed5412f64d8dc1580" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/93/72/6b3e70d32e89a5cbb6a4513726c1ae8762165b027af569289e19ec08edd8/typer-0.17.4-py3-none-any.whl", hash = "sha256:015534a6edaa450e7007eba705d5c18c3349dcea50a6ad79a5ed530967575824" }, +] + +[[package]] +name = "types-aiobotocore" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "botocore-stubs" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4d/b5/78bcb2a3e9c894178de71868d09e6295e703a06b730506f7dcc06c7042b3/types_aiobotocore-2.24.2.tar.gz", hash = "sha256:4d29ae0e848f20acebfcd229cdabecae3e1c13f13d9fcc9e9873254461413b1c" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/e8/23/1ff16d8db24e23f5c4d77dc4e3a2b8bfa6946644daf8ccbd4d2836b03175/types_aiobotocore-2.24.2-py3-none-any.whl", hash = "sha256:f5575dd692280a14788deb42c6dbcb0bea82c3223f02711c3437cda88f284238" }, +] + +[package.optional-dependencies] +essential = [ + { name = "types-aiobotocore-cloudformation" }, + { name = "types-aiobotocore-dynamodb" }, + { name = "types-aiobotocore-ec2" }, + { name = "types-aiobotocore-lambda" }, + { name = "types-aiobotocore-rds" }, + { name = "types-aiobotocore-s3" }, + { name = "types-aiobotocore-sqs" }, +] + +[[package]] +name = "types-aiobotocore-cloudformation" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/75/40/55ca417feb7b09feb4526068305059963ba87de7774d129c7d2d0397d889/types_aiobotocore_cloudformation-2.24.2.tar.gz", hash = "sha256:4fce60d7b24b0a9399e84df17b48f4450b9017a48a363bed868b1a91b18c2b51" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/02/8f/46b1ca9437b5a73cd521d90807d9c6aaf896cb249a553f2c516a367eed9f/types_aiobotocore_cloudformation-2.24.2-py3-none-any.whl", hash = "sha256:c5b144a2b03197e4d514921b0f557d0a98a33b338608456baf6d5f3dfb539807" }, +] + +[[package]] +name = "types-aiobotocore-dynamodb" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6f/bf/937ed279b5f1bfead80a83312688ab2b892ea64e4febed60cb20e22a3351/types_aiobotocore_dynamodb-2.24.2.tar.gz", hash = "sha256:83185d5eac180ece25252a24e59e7d3a2f113381cc4f0c2572622006749537ea" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/26/fe/ffa1c678d2f3e19a6e0b8b9cdd9ce55d6ca834d609f9491108b78364fa1f/types_aiobotocore_dynamodb-2.24.2-py3-none-any.whl", hash = "sha256:d2a92a4e52472953e02bd14516ed0b41c4ee3e855670d260705d5cf05ade99d0" }, +] + +[[package]] +name = "types-aiobotocore-ec2" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e7/d5/c1b45927e25ec44c311a15b7ea00db4baf4a259c09de4aa898f587804b00/types_aiobotocore_ec2-2.24.2.tar.gz", hash = "sha256:6a0205a30302711705f2d28b92c4a3734659f2695211ba4e0e14608e68a1be7f" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b3/1d/c7a91056477561695886da029f5435887f07dc09924995034f31e0277b0c/types_aiobotocore_ec2-2.24.2-py3-none-any.whl", hash = "sha256:6fba635d853a014a772dbf2c09ad54b75c73672d60d37c662527fd2def110fea" }, +] + +[[package]] +name = "types-aiobotocore-lambda" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cd/4e/d1e7aad1913050e3a710da144375e4618e457ff1020c896704cba7cdfe1d/types_aiobotocore_lambda-2.24.2.tar.gz", hash = "sha256:15d72eb9fdeb16699bd3edcc1b98d84f850fa0b5e0b5c777b999cc182bb470aa" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/38/41/6827b35b64aed4184b5f43ec065272f330305d02ebf6b0675630649c1adf/types_aiobotocore_lambda-2.24.2-py3-none-any.whl", hash = "sha256:5af3753fa65467937eaf6652d070d0ce1b30845ee5bac05936f813b4981639f5" }, +] + +[[package]] +name = "types-aiobotocore-rds" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/54/0f/77f32c2b2fd31c4b791bee90987d72dc5c1771e4f46c5c9e12999d6b64e3/types_aiobotocore_rds-2.24.2.tar.gz", hash = "sha256:5467ddaa0ff49fdcdbd51d4f1db614c16696b90d28d6caad8db11393e6df7335" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/08/d7/f8bb9954a834962cdcf2a957acbf8205d386aec73d6e4fd96f3d73b22966/types_aiobotocore_rds-2.24.2-py3-none-any.whl", hash = "sha256:f538fe294fe4826b014dd6f7b647f4d701e324531f78967379248ef21a76aa4b" }, +] + +[[package]] +name = "types-aiobotocore-s3" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/16/f7/278bd63254f296a8fef102a9e3b6281ad02b1e8a4bb702e21c5e201d9f50/types_aiobotocore_s3-2.24.2.tar.gz", hash = "sha256:7274bcca558385ef5f98037f5474f4583663634141d46d9d978ab7d37ca96324" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6d/10/4e09f98ba4d7c422ffd1b7291c8556fa66af0cabba4694e91fb6be077c47/types_aiobotocore_s3-2.24.2-py3-none-any.whl", hash = "sha256:b5541e994f9c5f41d551570be8788d4189fb55759f8f7d15a2893b29eae3114f" }, +] + +[[package]] +name = "types-aiobotocore-sqs" +version = "2.24.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b2/30/55a5b0aeb49de06dacb0810efc532fb7bcf04d6c252dfb7360c55cd2d98b/types_aiobotocore_sqs-2.24.2.tar.gz", hash = "sha256:a06c336c99e4e6bcb36ae5c57a38fd1d9c8d8a120a61ba85dbf280606239667e" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/13/61/94eb87cc919ceb8f9466279ccf036028dca733299a1b414c14ca9ca66a44/types_aiobotocore_sqs-2.24.2-py3-none-any.whl", hash = "sha256:929fcf0fd6d25ee683d70bf7367534f434f7c15c379328198f8df0b417e4458b" }, +] + +[[package]] +name = "types-awscrt" +version = "0.27.6" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/56/ce/5d84526a39f44c420ce61b16654193f8437d74b54f21597ea2ac65d89954/types_awscrt-0.27.6.tar.gz", hash = "sha256:9d3f1865a93b8b2c32f137514ac88cb048b5bc438739945ba19d972698995bfb" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ac/af/e3d20e3e81d235b3964846adf46a334645a8a9b25a0d3d472743eb079552/types_awscrt-0.27.6-py3-none-any.whl", hash = "sha256:18aced46da00a57f02eb97637a32e5894dc5aa3dc6a905ba3e5ed85b9f3c526b" }, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20250822" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0c/0a/775f8551665992204c756be326f3575abba58c4a3a52eef9909ef4536428/types_python_dateutil-2.9.0.20250822.tar.gz", hash = "sha256:84c92c34bd8e68b117bff742bc00b692a1e8531262d4507b33afcc9f7716cd53" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ab/d9/a29dfa84363e88b053bf85a8b7f212a04f0d7343a4d24933baa45c06e08b/types_python_dateutil-2.9.0.20250822-py3-none-any.whl", hash = "sha256:849d52b737e10a6dc6621d2bd7940ec7c65fcb69e6aa2882acf4e56b2b508ddc" }, +] + +[[package]] +name = "types-s3transfer" +version = "0.13.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a5/c5/23946fac96c9dd5815ec97afd1c8ad6d22efa76c04a79a4823f2f67692a5/types_s3transfer-0.13.1.tar.gz", hash = "sha256:ce488d79fdd7d3b9d39071939121eca814ec65de3aa36bdce1f9189c0a61cc80" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/8e/dc/b3f9b5c93eed6ffe768f4972661250584d5e4f248b548029026964373bcd/types_s3transfer-0.13.1-py3-none-any.whl", hash = "sha256:4ff730e464a3fd3785b5541f0f555c1bd02ad408cf82b6b7a95429f6b0d26b4a" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" }, +] + +[[package]] +name = "userpath" +version = "1.9.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d5/b7/30753098208505d7ff9be5b3a32112fb8a4cb3ddfccbbb7ba9973f2e29ff/userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/43/99/3ec6335ded5b88c2f7ed25c56ffd952546f7ed007ffb1e1539dc3b57015a/userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d" }, +] + +[[package]] +name = "uv" +version = "0.8.15" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4f/7c/ab905b0425f88842f3d8e5da50491524f45a231b7a3dc9c988608162adb2/uv-0.8.15.tar.gz", hash = "sha256:8ea57b78be9f0911a2a50b6814d15aec7d1f8aa6517059dc8250b1414156f93a" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/67/1d/794352a01b40f2b0a0abe02f4f020219b3f59ee6ed900561be3b2b47a82b/uv-0.8.15-py3-none-linux_armv6l.whl", hash = "sha256:f02e6b8be08b840f86b8d5997b658b657acdda95bc216ecf62fce6c71414bdc7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8f/89/528f01cff01eb8d10dd396f437656266443e399dda2fe4787b2cf6983698/uv-0.8.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b0461bb1ad616c8bcb59c9b39ae9363245ca33815ebb1d11130385236eca21b9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/94/03/532af32a64d162894a1daebb7bc5028ba00225ea720cf0f287e934dc2bd5/uv-0.8.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:069eed78b79d1e88bced23e3d4303348edb0a0209e7cae0f20024c42430bf50f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/25/21/57df6d53fbadfa947d9d65a0926e5d8540199f49aa958d23be2707262a80/uv-0.8.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:333a93bb6af64f3b95ee99e82b4ea227e2af6362c45f91c89a24e2bfefb628f9" }, + { url = "https://mirrors.aliyun.com/pypi/packages/68/22/c3784749e1c78119e5375ec34c6ea29e944192a601f17c746339611db237/uv-0.8.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7d5b19ac2bdda3d1456b5d6013af50b443ffb0e40c66d42874f71190a5364711" }, + { url = "https://mirrors.aliyun.com/pypi/packages/00/28/0597599fb35408dd73e0a7d25108dca1fa6ce8f8d570c8f24151b0016eef/uv-0.8.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3330bb4f206a6180679a75a8b2e77ff0f933fcb06c028b6f4da877b10a5e4f95" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4f/61/98fa07981722660f5a3c28b987df99c2486f63d01b1256e6cca05a43bdce/uv-0.8.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:de9896ad4fa724ab317a8048f4891b9b23df1403b3724e96606f3be2dbbbf009" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fa/65/523188e11a759144b00f0fe48943f6d00706fcd9b5f561a54a07b9fd4541/uv-0.8.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:226360003e71084e0a73cbec72170e88634b045e95529654d067ea3741bba242" }, + { url = "https://mirrors.aliyun.com/pypi/packages/99/3c/7898acf3d9ed2d3a2986cccc8209c14d3e9ac72dfaa616e49d329423b1d3/uv-0.8.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9488260536b35b94a79962fea76837f279c0cd0ae5021c761e66b311f47ffa70" }, + { url = "https://mirrors.aliyun.com/pypi/packages/13/fc/e0da45ee179367dcc1e1040ad00ed8a99b78355d43024b0b5fc2edf5c389/uv-0.8.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07765f99fd5fd3b257d7e210e8d0844c0a8fd111612e31fcca66a85656cc728e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ce/5d/180904fa7ed49081b27f00e86f7220ca62cc098d7ef6459f0c69a8ae8f74/uv-0.8.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c4868e6a4e1a8c777a5ba3cff452c405837318fb0b272ff203bfda0e1b8fc54d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b6/09/fed823212e695b6765bdb8462850abffbe685cd965c4de905efed5e2e5c9/uv-0.8.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3ec78a54a8eb0bbb9a9c653982390af84673657c8a48a0be6cdcb81d7d3e95c3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b9/f3/9c4211897c00f79b7973a10800166e0580eaad20fe27f7c06adb7b248ac7/uv-0.8.15-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a022a752d20da80d2a49fc0721522a81e3a32efe539152d756d84ebdba29dbc3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/3b/43/4ec6047150e2fba494d80d36b881a1a973835afa497ae9ccdf51828cae4f/uv-0.8.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:3780d2f3951d83e55812fdeb7eee233787b70c774497dbfc55b0fdf6063aa345" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ea/98/b4220bf462fb225c4a2d74ef4f105020238472b4b0da94ebc17a310d7b4e/uv-0.8.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:56f2451c9193ee1754ce1d8390ded68e9cb8dee0aaf7e2f38a9bd04d99be1be7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5e/b8/40ce3d385254ac87a664a5d9a4664fac697e2734352f404382b81d03235b/uv-0.8.15-py3-none-win32.whl", hash = "sha256:89c7c10089e07d944c72d388fd88666c650dec2f8c79ca541e365f32843882c6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2b/9d/081a0af395c0e307c0c930e80161a2aa551c25064cfb636d060574566fa4/uv-0.8.15-py3-none-win_amd64.whl", hash = "sha256:6aa824ab933dfafe11efe32e6541c6bcd65ecaa927e8e834ea6b14d3821020f6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/30/47/d8f50264a8c8ebbb9a44a8fed08b6e873d943adf299d944fe3a776ff5fbf/uv-0.8.15-py3-none-win_arm64.whl", hash = "sha256:a395fa1fc8948eacdd18e4592ed489fad13558b13fea6b3544cb16e5006c5b02" }, +] + +[[package]] +name = "uvicorn" +version = "0.35.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.21.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6" }, + { url = "https://mirrors.aliyun.com/pypi/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d" }, +] + +[[package]] +name = "virtualenv" +version = "20.34.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12" }, + { url = "https://mirrors.aliyun.com/pypi/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431" }, + { url = "https://mirrors.aliyun.com/pypi/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57" }, + { url = "https://mirrors.aliyun.com/pypi/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792" }, + { url = "https://mirrors.aliyun.com/pypi/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85" }, + { url = "https://mirrors.aliyun.com/pypi/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065" }, + { url = "https://mirrors.aliyun.com/pypi/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f" }, +] + +[[package]] +name = "wrapt" +version = "1.17.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85" }, + { url = "https://mirrors.aliyun.com/pypi/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311" }, + { url = "https://mirrors.aliyun.com/pypi/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2" }, + { url = "https://mirrors.aliyun.com/pypi/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89" }, + { url = "https://mirrors.aliyun.com/pypi/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77" }, + { url = "https://mirrors.aliyun.com/pypi/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22" }, +] + +[[package]] +name = "yarl" +version = "1.20.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4" }, + { url = "https://mirrors.aliyun.com/pypi/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8" }, + { url = "https://mirrors.aliyun.com/pypi/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845" }, + { url = "https://mirrors.aliyun.com/pypi/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1" }, + { url = "https://mirrors.aliyun.com/pypi/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e" }, +] + +[[package]] +name = "zstandard" +version = "0.24.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68" }, + { url = "https://mirrors.aliyun.com/pypi/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13" }, + { url = "https://mirrors.aliyun.com/pypi/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382" }, + { url = "https://mirrors.aliyun.com/pypi/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e" }, + { url = "https://mirrors.aliyun.com/pypi/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186" }, + { url = "https://mirrors.aliyun.com/pypi/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c" }, + { url = "https://mirrors.aliyun.com/pypi/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db" }, + { url = "https://mirrors.aliyun.com/pypi/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848" }, + { url = "https://mirrors.aliyun.com/pypi/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34" }, +]