├── .python-version ├── app ├── __init__.py ├── api │ ├── v1 │ │ ├── __init__.py │ │ ├── models.py │ │ └── chat.py │ └── dependencies.py ├── utils │ ├── __init__.py │ ├── stream_parser.py │ ├── http_client.py │ ├── logger.py │ └── file_utils.py ├── models │ └── schemas.py ├── main.py ├── config.py └── services │ ├── chat_service.py │ └── kiira_client.py ├── .gitignore ├── main.py ├── pyproject.toml ├── .env.example ├── .dockerignore ├── docker-compose.yml ├── Dockerfile ├── .github └── workflows │ └── docker-publish.yml ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.11 2 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Kiira2API 应用包 3 | """ 4 | __version__ = "0.1.0" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | data/ 7 | wheels/ 8 | *.egg-info 9 | 10 | # Virtual environments 11 | .venv 12 | .env 13 | memory_bank.md -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | 兼容入口:从 app.main 导入应用 3 | 保留此文件以便使用 uvicorn main:app 启动 4 | """ 5 | 6 | from app.main import app 7 | 8 | if __name__ == "__main__": 9 | import uvicorn 10 | uvicorn.run(app, host="0.0.0.0", port=8999) 11 | -------------------------------------------------------------------------------- /app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | API v1 路由模块 3 | """ 4 | from fastapi import APIRouter 5 | from . import chat, models 6 | router = APIRouter(prefix="/v1") 7 | 8 | router.include_router(chat.router) 9 | router.include_router(models.router) 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "kiira2api" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | requires-python = ">=3.11" 6 | dependencies = [ 7 | "fastapi>=0.121.2", 8 | "requests>=2.32.5", 9 | "uvicorn[standard]>=0.38.0", 10 | "pydantic-settings>=2.0.0", 11 | ] 12 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # API Key 配置(用于接口鉴权) 2 | # 如果使用默认值 sk-123456,将跳过鉴权验证 3 | API_KEY=sk-123456 4 | 5 | # Kiira AI API 基础 URL 6 | BASE_URL_KIIRA=https://www.kiira.ai 7 | 8 | # SeaArt API 基础 URL 9 | BASE_URL_SEAART_API=https://app-matrix-api.api.seaart.ai 10 | BASE_URL_SEAART_UPLOADER=https://aiart-uploader.api.seaart.dev 11 | 12 | # 默认配置 13 | DEFAULT_AGENT_NAME=Nano Banana Pro 🔥👉 Try Free 14 | DEFAULT_CATEGORY=74 15 | 16 | # Agent 列表(支持 JSON 或逗号分隔格式) 17 | # JSON 格式(推荐): 18 | AGENT_LIST=["Nano Banana Pro 🔥👉 Try Free","Nano Banana Video Pro🔥","Sora 2: AI Video Remixer", "Veo 3: AI Video Weaver", "Midjourney Art Studio"] 19 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | *.egg-info/ 8 | dist/ 9 | build/ 10 | .venv/ 11 | venv/ 12 | env/ 13 | ENV/ 14 | 15 | # IDE 16 | .vscode/ 17 | .idea/ 18 | *.swp 19 | *.swo 20 | *~ 21 | 22 | # Git 23 | .git/ 24 | .gitignore 25 | .gitattributes 26 | 27 | # Docker 28 | Dockerfile 29 | .dockerignore 30 | docker-compose.yml 31 | 32 | # 文档 33 | *.md 34 | !README.md 35 | docs/ 36 | 37 | # 测试 38 | .pytest_cache/ 39 | .coverage 40 | htmlcov/ 41 | .tox/ 42 | 43 | # 日志 44 | *.log 45 | logs/ 46 | 47 | # 环境变量文件(可选,如果需要可以在运行时挂载) 48 | .env 49 | .env.local 50 | .env.*.local 51 | 52 | # 其他 53 | .DS_Store 54 | *.bak 55 | *.tmp 56 | 57 | data/ -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | kiira2api: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | container_name: kiira2api 9 | ports: 10 | - "8999:8999" 11 | volumes: 12 | - ./data:/app/data 13 | env_file: 14 | - .env 15 | environment: 16 | - PYTHONUNBUFFERED=1 17 | - PYTHONDONTWRITEBYTECODE=1 18 | restart: unless-stopped 19 | healthcheck: 20 | test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8999/health')"] 21 | interval: 30s 22 | timeout: 10s 23 | retries: 3 24 | start_period: 5s 25 | networks: 26 | - kiira2api-network 27 | 28 | networks: 29 | kiira2api-network: 30 | driver: bridge 31 | 32 | -------------------------------------------------------------------------------- /app/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 工具函数模块 3 | """ 4 | from .http_client import build_headers, make_request 5 | from .file_utils import ( 6 | guess_content_type, 7 | get_file_extension_from_content_type, 8 | decode_base64_img, 9 | get_image_data_and_type 10 | ) 11 | from .stream_parser import parse_stream_response 12 | from .logger import get_logger, setup_logger, configure_root_logger 13 | 14 | __all__ = [ 15 | 'build_headers', 16 | 'make_request', 17 | 'guess_content_type', 18 | 'get_file_extension_from_content_type', 19 | 'decode_base64_img', 20 | 'get_image_data_and_type', 21 | 'parse_stream_response', 22 | 'get_logger', 23 | 'setup_logger', 24 | 'configure_root_logger', 25 | ] 26 | 27 | # 注意:config_loader 不在 __init__.py 中导入,避免循环导入 28 | # 如需使用,请直接从 app.utils.config_loader 导入 -------------------------------------------------------------------------------- /app/api/v1/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | 模型相关 API 路由 3 | """ 4 | from fastapi import APIRouter, Depends 5 | from typing import Optional 6 | from app.models.schemas import ModelInfo 7 | from app.config import AGENT_LIST 8 | from app.api.dependencies import verify_api_key 9 | 10 | router = APIRouter(tags=["models"]) 11 | 12 | @router.get("/models") 13 | async def get_models(api_key: Optional[str] = Depends(verify_api_key)): 14 | """ 15 | Get available models endpoint compatible with OpenAI API format. 16 | """ 17 | models = [] 18 | for agent in AGENT_LIST: 19 | models.append( 20 | ModelInfo( 21 | id=agent, 22 | object="model", 23 | created=1677610602, 24 | owned_by="move132" 25 | ) 26 | ) 27 | return { 28 | "object": "list", 29 | "data": models 30 | } 31 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用 Python 3.11 作为基础镜像 2 | FROM python:3.11-slim as builder 3 | 4 | # 设置工作目录 5 | WORKDIR /app 6 | 7 | # 安装 uv 8 | COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv 9 | 10 | # 复制依赖文件 11 | COPY pyproject.toml uv.lock ./ 12 | 13 | # 安装依赖到虚拟环境 14 | RUN uv sync --frozen --no-dev 15 | 16 | # 运行阶段 17 | FROM python:3.11-slim 18 | 19 | # 设置工作目录 20 | WORKDIR /app 21 | 22 | # 安装 uv(用于运行时) 23 | COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv 24 | 25 | # 从构建阶段复制虚拟环境 26 | COPY --from=builder /app/.venv /app/.venv 27 | 28 | # 复制应用代码 29 | COPY . . 30 | 31 | # 设置环境变量 32 | ENV PATH="/app/.venv/bin:$PATH" \ 33 | PYTHONUNBUFFERED=1 \ 34 | PYTHONDONTWRITEBYTECODE=1 35 | 36 | # 暴露端口 37 | EXPOSE 8999 38 | 39 | VOLUME ["/app/data"] 40 | # 健康检查 41 | HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ 42 | CMD python -c "import requests; requests.get('http://localhost:8999/health')" || exit 1 43 | 44 | # 启动命令 45 | CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8999"] 46 | 47 | -------------------------------------------------------------------------------- /app/models/schemas.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pydantic 数据模型定义 3 | """ 4 | from pydantic import BaseModel 5 | from typing import List, Optional, Dict, Any, Union 6 | 7 | class ChatMessage(BaseModel): 8 | """聊天消息模型""" 9 | role: str 10 | content: Union[str, List[Dict[str, Any]]] # 支持文本或多模态内容(文本+图片) 11 | group_id: Optional[str] = None 12 | 13 | 14 | class ChatCompletionRequest(BaseModel): 15 | """聊天完成请求模型""" 16 | model: str 17 | messages: List[ChatMessage] 18 | temperature: Optional[float] = 1.0 19 | max_tokens: Optional[int] = None 20 | stream: Optional[bool] = False 21 | 22 | 23 | class ChatCompletionResponse(BaseModel): 24 | """聊天完成响应模型""" 25 | id: str 26 | object: str = "chat.completion" 27 | created: int 28 | model: str 29 | choices: List[Dict[str, Any]] 30 | usage: Optional[Dict[str, int]] = None 31 | 32 | 33 | class ModelInfo(BaseModel): 34 | """模型信息模型""" 35 | id: str 36 | object: str = "model" 37 | created: int 38 | owned_by: str 39 | 40 | 41 | class ModelsResponse(BaseModel): 42 | """模型列表响应模型""" 43 | object: str = "list" 44 | data: List[ModelInfo] 45 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | tags: 9 | - 'v*' 10 | workflow_dispatch: 11 | 12 | env: 13 | REGISTRY: ghcr.io 14 | IMAGE_NAME: ${{ github.repository }} 15 | 16 | jobs: 17 | build-and-push: 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | - name: Set up Docker Buildx 28 | uses: docker/setup-buildx-action@v3 29 | 30 | - name: Log in to Container Registry 31 | uses: docker/login-action@v3 32 | with: 33 | registry: ${{ env.REGISTRY }} 34 | username: ${{ github.actor }} 35 | password: ${{ secrets.GITHUB_TOKEN }} 36 | 37 | - name: Extract metadata (tags, labels) for Docker 38 | id: meta 39 | uses: docker/metadata-action@v5 40 | with: 41 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 42 | tags: | 43 | type=ref,event=branch 44 | type=semver,pattern={{version}} 45 | type=semver,pattern={{major}}.{{minor}} 46 | type=semver,pattern={{major}} 47 | type=raw,value=latest,enable={{is_default_branch}} 48 | type=sha,prefix={{branch}}- 49 | 50 | - name: Build and push Docker image 51 | uses: docker/build-push-action@v5 52 | with: 53 | context: . 54 | file: ./Dockerfile 55 | push: true 56 | tags: ${{ steps.meta.outputs.tags }} 57 | labels: ${{ steps.meta.outputs.labels }} 58 | cache-from: type=gha 59 | cache-to: type=gha,mode=max 60 | platforms: linux/amd64,linux/arm64 61 | 62 | -------------------------------------------------------------------------------- /app/utils/stream_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | 流式响应解析工具 3 | 优化JSON解析,避免重复解析同一数据 4 | """ 5 | import json 6 | from typing import Optional, Dict, Any 7 | 8 | from app.utils.logger import get_logger 9 | 10 | logger = get_logger(__name__) 11 | 12 | 13 | def extract_media_from_data(data: Dict[str, Any]) -> Optional[tuple[str, str]]: 14 | """ 15 | 从已解析的数据中提取媒体URL 16 | 避免重复JSON解析,提升性能 17 | 18 | Args: 19 | data: 已解析的JSON数据字典 20 | 21 | Returns: 22 | (url, type) tuple 如果找到媒体,否则返回None 23 | """ 24 | try: 25 | # 在choices数组->每个choice下的sa_resources数组中查找type为"video"或"image"且有"url" 26 | if "choices" in data and isinstance(data["choices"], list): 27 | for choice in data["choices"]: 28 | if isinstance(choice, dict) and "sa_resources" in choice and isinstance(choice["sa_resources"], list): 29 | for resource in choice["sa_resources"]: 30 | if ( 31 | isinstance(resource, dict) 32 | and resource.get("type") in ("video", "image") 33 | and "url" in resource 34 | and resource["url"] 35 | ): 36 | return resource["url"], resource["type"] 37 | except Exception as e: 38 | logger.debug(f"提取媒体URL时出错: {e}") 39 | 40 | return None 41 | 42 | 43 | def parse_stream_response(line: str) -> Optional[tuple[str, str]]: 44 | """ 45 | 解析流式响应中的媒体URL(兼容旧接口) 46 | 建议使用extract_media_from_data以避免重复JSON解析 47 | 48 | Args: 49 | line: SSE格式的响应行 50 | 51 | Returns: 52 | (url, type) tuple 如果找到媒体,否则返回None 53 | """ 54 | if not line.startswith("data: "): 55 | return None 56 | 57 | try: 58 | json_str = line[6:] # 移除 "data: " 前缀 59 | data = json.loads(json_str) 60 | return extract_media_from_data(data) 61 | except json.JSONDecodeError: 62 | pass 63 | except Exception as e: 64 | logger.debug(f"解析响应时出错: {e}") 65 | 66 | return None 67 | 68 | -------------------------------------------------------------------------------- /app/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | FastAPI 应用主入口 3 | """ 4 | import uvicorn 5 | import json 6 | import logging 7 | from contextlib import asynccontextmanager 8 | from fastapi import FastAPI 9 | from app.api.v1 import router as v1_router 10 | from app.utils.logger import configure_root_logger, get_logger 11 | from app.config import API_KEY, AGENT_LIST, DEFAULT_AGENT_NAME 12 | # 配置根日志记录器(彩色输出) 13 | configure_root_logger(level=logging.INFO, use_color=True) 14 | 15 | logger = get_logger(__name__) 16 | # 定义一些颜色代码 17 | CYAN = "\033[36m" 18 | GREEN = "\033[32m" 19 | YELLOW = "\033[33m" 20 | MAGENTA = "\033[35m" 21 | RESET = "\033[0m" 22 | # 紫色 23 | RED = "\033[31m" 24 | ORANGE = "\033[38;5;208m" 25 | 26 | settings = { 27 | "API_KEY": API_KEY, 28 | "AGENT_LIST": AGENT_LIST, 29 | "DEFAULT_AGENT_NAME": DEFAULT_AGENT_NAME 30 | } 31 | project_logo_str = fr"""{GREEN} 32 | ██ ▄█▀ ▄▄ ▄▄ ▄▄▄▄ ▄▄▄ ████▄ ▄████▄ █████▄ ██ 33 | ████ ██ ██ ██▄█▄ ██▀██ ▄██▀ ██▄▄██ ██▄▄█▀ ██ 34 | ██ ▀█▄ ██ ██ ██ ██ ██▀██ ███▄▄ ██ ██ ██ ██ 35 | -------------------------------------------------- 36 | 37 | Kiira2API - 基于Kiira AI的逆向API服务 38 | 项目地址 : https://github.com/move132/kiira2api 39 | 作者 : move132 40 | 版本 : 1.0.0 41 | 当前环境变量信息 42 | {json.dumps(settings, ensure_ascii=False, indent=2)} 43 | """ 44 | @asynccontextmanager 45 | async def lifespan(app: FastAPI): 46 | """应用生命周期管理""" 47 | # 启动时执行 48 | print(f"{GREEN}{'=' * 50}{RESET}") 49 | print(f"{GREEN}🚀Kiira2API 启动成功{RESET}") 50 | print(project_logo_str) 51 | print(f"{GREEN}{'=' * 50}{RESET}") 52 | yield 53 | 54 | app = FastAPI(title="Kiira2API", version="1.0.0", lifespan=lifespan) 55 | 56 | # 注册 API 路由 57 | app.include_router(v1_router) 58 | 59 | 60 | @app.get("/") 61 | async def root(): 62 | """根路径,返回 API 基本信息""" 63 | return {"message": "Kiira2API is running", "version": "1.0.0", "author": "move132", "project_url": "https://github.com/move132/kiira2api"} 64 | 65 | 66 | @app.get("/health") 67 | async def health(): 68 | """健康检查端点""" 69 | return {"status": "healthy"} 70 | 71 | 72 | if __name__ == "__main__": 73 | uvicorn.run(app, host="0.0.0.0", port=8999, log_level="info") -------------------------------------------------------------------------------- /app/api/dependencies.py: -------------------------------------------------------------------------------- 1 | """ 2 | API 依赖项 3 | """ 4 | from typing import Optional 5 | from fastapi import Header, HTTPException, status 6 | from app.config import API_KEY 7 | from app.utils.logger import get_logger 8 | 9 | logger = get_logger(__name__) 10 | 11 | 12 | async def verify_api_key( 13 | authorization: Optional[str] = Header(None, alias="Authorization"), 14 | x_api_key: Optional[str] = Header(None, alias="X-API-Key") 15 | ) -> Optional[str]: 16 | """ 17 | 验证 API Key 18 | 19 | 支持两种方式: 20 | 1. Authorization: Bearer (OpenAI 兼容格式) 21 | 2. X-API-Key: (备用方式) 22 | 23 | Args: 24 | authorization: Authorization header 25 | x_api_key: X-API-Key header 26 | 27 | Returns: 28 | Optional[str]: 验证通过的 API Key,如果使用默认值则返回 None 29 | 30 | Raises: 31 | HTTPException: API Key 无效或缺失 32 | """ 33 | # 如果未配置 API_KEY,跳过验证 34 | if not API_KEY: 35 | logger.warning("API_KEY 未配置或使用默认值,跳过鉴权验证") 36 | return None 37 | 38 | api_key = None 39 | # 方式 1: 从 Authorization header 获取 (Bearer token) 40 | if authorization: 41 | try: 42 | scheme, token = authorization.split(" ", 1) 43 | if scheme.lower() == "bearer": 44 | api_key = token 45 | except ValueError: 46 | pass 47 | 48 | # 方式 2: 从 X-API-Key header 获取 49 | if not api_key and x_api_key: 50 | api_key = x_api_key 51 | 52 | # 验证 API Key 53 | if not api_key: 54 | logger.warning("请求缺少 API Key") 55 | raise HTTPException( 56 | status_code=status.HTTP_401_UNAUTHORIZED, 57 | detail="Missing API Key. Please provide API Key via Authorization header (Bearer token) or X-API-Key header.", 58 | headers={"WWW-Authenticate": "Bearer"}, 59 | ) 60 | 61 | if api_key != API_KEY: 62 | logger.warning(f"API Key 验证失败: {api_key[:10]}...") 63 | raise HTTPException( 64 | status_code=status.HTTP_401_UNAUTHORIZED, 65 | detail="Invalid API Key", 66 | headers={"WWW-Authenticate": "Bearer"}, 67 | ) 68 | 69 | logger.debug("API Key 验证通过") 70 | return api_key 71 | 72 | -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | 应用配置 3 | 使用 pydantic-settings 从环境变量或 .env 文件中读取配置 4 | """ 5 | import json 6 | from typing import List 7 | from pydantic import Field, field_validator 8 | from pydantic_settings import BaseSettings, SettingsConfigDict 9 | 10 | class Settings(BaseSettings): 11 | """应用配置类""" 12 | 13 | model_config = SettingsConfigDict( 14 | env_file='.env', 15 | env_file_encoding='utf-8', 16 | case_sensitive=False, 17 | extra='ignore', # 忽略未定义的字段 18 | ) 19 | 20 | # Kiira AI API 基础 URL 21 | base_url_kiira: str = Field( 22 | default='https://www.kiira.ai', 23 | alias='BASE_URL_KIIRA', 24 | description='Kiira AI API 基础 URL' 25 | ) 26 | 27 | # SeaArt API 基础 URL 28 | base_url_seaart_api: str = Field( 29 | default='https://app-matrix-api.api.seaart.ai', 30 | alias='BASE_URL_SEAART_API', 31 | description='SeaArt API 基础 URL' 32 | ) 33 | 34 | base_url_seaart_uploader: str = Field( 35 | default='https://aiart-uploader.api.seaart.dev', 36 | alias='BASE_URL_SEAART_UPLOADER', 37 | description='SeaArt Uploader API 基础 URL' 38 | ) 39 | # api key 40 | api_key: str = Field( 41 | default='sk-123456', 42 | alias='API_KEY', 43 | description='API Key' 44 | ) 45 | # 默认配置 46 | default_agent_name: str = Field( 47 | default='Nano Banana Pro🔥', 48 | alias='DEFAULT_AGENT_NAME', 49 | description='默认代理名称' 50 | ) 51 | 52 | # Agent 列表配置 53 | # 支持两种格式: 54 | # 1. JSON 格式:AGENT_LIST=["Dress up Game", "NanoBanana PlayLab"] 55 | # 2. 逗号分隔:AGENT_LIST=Dress up Game,NanoBanana PlayLab 56 | agent_list: List[str] = Field( 57 | default_factory=list, 58 | alias='AGENT_LIST', 59 | description='Agent 列表配置' 60 | ) 61 | 62 | @field_validator('agent_list', mode='before') 63 | @classmethod 64 | def parse_agent_list(cls, v): 65 | """解析 Agent 列表,支持 JSON 和逗号分隔格式""" 66 | if v is None: 67 | return [] 68 | 69 | # 如果是列表,直接返回 70 | if isinstance(v, list): 71 | return v 72 | 73 | # 如果是字符串 74 | if isinstance(v, str): 75 | v = v.strip() 76 | # 尝试解析为 JSON 77 | if v.startswith('[') and v.endswith(']'): 78 | try: 79 | return json.loads(v) 80 | except json.JSONDecodeError: 81 | pass 82 | 83 | # 否则按逗号分隔 84 | if v: 85 | return [item.strip() for item in v.split(',') if item.strip()] 86 | 87 | return [] 88 | 89 | 90 | # 创建全局配置实例 91 | settings = Settings() 92 | 93 | # 为了向后兼容,导出原有的变量名(大写) 94 | BASE_URL_KIIRA = settings.base_url_kiira 95 | BASE_URL_SEAART_API = settings.base_url_seaart_api 96 | BASE_URL_SEAART_UPLOADER = settings.base_url_seaart_uploader 97 | DEFAULT_AGENT_NAME = settings.default_agent_name 98 | AGENT_LIST = settings.agent_list 99 | API_KEY = settings.api_key 100 | 101 | # 导出配置类和实例,方便高级用法 102 | __all__ = [ 103 | 'Settings', 104 | 'settings', 105 | 'BASE_URL_KIIRA', 106 | 'BASE_URL_SEAART_API', 107 | 'BASE_URL_SEAART_UPLOADER', 108 | 'DEFAULT_AGENT_NAME', 109 | 'AGENT_LIST', 110 | 'API_KEY', 111 | ] 112 | -------------------------------------------------------------------------------- /app/utils/http_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP 客户端工具函数 3 | 提供复用连接的HTTP Session和统一超时管理 4 | """ 5 | import json 6 | import requests 7 | from typing import Optional, Dict, Any, Tuple 8 | 9 | from app.config import BASE_URL_KIIRA 10 | from app.utils.logger import get_logger 11 | 12 | logger = get_logger(__name__) 13 | 14 | # 全局Session单例,复用HTTP连接以提升性能 15 | _session: Optional[requests.Session] = None 16 | 17 | # 默认超时配置: (连接超时, 读取超时) 秒 18 | DEFAULT_TIMEOUT: Tuple[int, int] = (3, 15) 19 | 20 | 21 | def build_headers( 22 | device_id: str, 23 | token: Optional[str] = None, 24 | referer: Optional[str] = None, 25 | content_type: str = 'application/json', 26 | accept: str = '*/*', 27 | accept_language: str = 'zh,zh-CN;q=0.9,en;q=0.8,ja;q=0.7', 28 | sec_fetch_site: str = 'same-origin' 29 | ) -> Dict[str, str]: 30 | """ 31 | 构建通用请求头 32 | 33 | Args: 34 | device_id: 设备ID 35 | token: 认证token 36 | referer: Referer头 37 | content_type: Content-Type 38 | accept: Accept头 39 | accept_language: Accept-Language头 40 | sec_fetch_site: Sec-Fetch-Site头 41 | 42 | Returns: 43 | 请求头字典 44 | """ 45 | 46 | headers = { 47 | 'accept': accept, 48 | 'accept-language': accept_language, 49 | 'cache-control': 'no-cache', 50 | 'content-type': content_type, 51 | 'origin': BASE_URL_KIIRA, 52 | 'pragma': 'no-cache', 53 | 'priority': 'u=1, i', 54 | 'referer': referer or BASE_URL_KIIRA, 55 | 'sec-fetch-dest': 'empty', 56 | 'sec-fetch-mode': 'cors', 57 | 'sec-fetch-site': sec_fetch_site, 58 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', 59 | 'x-app-id': 'gen.seagen.app', 60 | 'x-device-id': device_id, 61 | 'x-language': 'en', 62 | 'x-platform': 'web' 63 | } 64 | 65 | if token: 66 | headers['token'] = token 67 | 68 | return headers 69 | 70 | 71 | def get_session() -> requests.Session: 72 | """ 73 | 获取全局HTTP Session单例 74 | Session会复用TCP连接,减少握手开销 75 | 76 | Returns: 77 | requests.Session实例 78 | """ 79 | global _session 80 | if _session is None: 81 | _session = requests.Session() 82 | # 设置连接池大小 83 | adapter = requests.adapters.HTTPAdapter( 84 | pool_connections=10, 85 | pool_maxsize=20, 86 | pool_block=False 87 | ) 88 | _session.mount('http://', adapter) 89 | _session.mount('https://', adapter) 90 | return _session 91 | 92 | 93 | def make_request( 94 | method: str, 95 | url: str, 96 | device_id: str, 97 | token: Optional[str] = None, 98 | headers: Optional[Dict[str, str]] = None, 99 | json_data: Optional[Dict[str, Any]] = None, 100 | timeout: Optional[Tuple[int, int]] = None, 101 | **kwargs 102 | ) -> Optional[Dict[str, Any]]: 103 | """ 104 | 发送HTTP请求的通用方法 105 | 使用全局Session复用连接,提升性能 106 | 107 | Args: 108 | method: HTTP方法 109 | url: 请求URL 110 | device_id: 设备ID 111 | token: 认证token 112 | headers: 自定义请求头(如果提供,将覆盖默认头) 113 | json_data: JSON数据 114 | timeout: 超时配置(连接超时,读取超时),默认(3,15)秒 115 | **kwargs: 其他requests参数 116 | 117 | Returns: 118 | 响应JSON数据,失败返回None 119 | """ 120 | if headers is None: 121 | headers = build_headers(device_id=device_id, token=token) 122 | 123 | # 使用默认超时 124 | if timeout is None: 125 | timeout = DEFAULT_TIMEOUT 126 | 127 | try: 128 | session = get_session() 129 | response = session.request( 130 | method=method, 131 | url=url, 132 | headers=headers, 133 | json=json_data, 134 | timeout=timeout, 135 | **kwargs 136 | ) 137 | response.raise_for_status() 138 | return response.json() 139 | except requests.exceptions.Timeout as e: 140 | logger.error(f"请求超时 {method} {url}: {e}") 141 | return None 142 | except requests.exceptions.RequestException as e: 143 | logger.error(f"请求失败 {method} {url}: {e}") 144 | return None 145 | except json.JSONDecodeError as e: 146 | logger.error(f"JSON解析失败: {e}") 147 | return None 148 | except Exception as e: 149 | logger.error(f"未知错误: {e}", exc_info=True) 150 | return None 151 | -------------------------------------------------------------------------------- /app/utils/logger.py: -------------------------------------------------------------------------------- 1 | """ 2 | 统一日志处理模块,支持彩色输出 3 | """ 4 | import logging 5 | import sys 6 | from typing import Optional 7 | 8 | 9 | class ColoredFormatter(logging.Formatter): 10 | """彩色日志格式化器""" 11 | 12 | # ANSI 颜色代码 13 | COLORS = { 14 | 'DEBUG': '\033[36m', # 青色 15 | 'INFO': '\033[32m', # 绿色 16 | 'WARNING': '\033[33m', # 黄色 17 | 'ERROR': '\033[31m', # 红色 18 | 'CRITICAL': '\033[35m', # 紫色 19 | 'RESET': '\033[0m', # 重置 20 | } 21 | 22 | def __init__(self, use_color: bool = True, *args, **kwargs): 23 | super().__init__(*args, **kwargs) 24 | self.use_color = use_color and sys.stdout.isatty() 25 | 26 | def format(self, record: logging.LogRecord) -> str: 27 | """格式化日志记录""" 28 | if self.use_color: 29 | # 保存原始值 30 | original_levelname = record.levelname 31 | 32 | # 获取颜色 33 | color = self.COLORS.get(record.levelname, self.COLORS['RESET']) 34 | reset = self.COLORS['RESET'] 35 | 36 | # 给级别名称添加颜色 37 | record.levelname = f"{color}{record.levelname}{reset}" 38 | 39 | # 先获取格式化后的消息内容(这样能正确处理所有格式化情况) 40 | formatted_message = record.getMessage() 41 | 42 | # 给消息内容添加颜色 43 | # 临时替换消息,以便在格式化时包含颜色 44 | original_msg = record.msg 45 | original_args = record.args 46 | record.msg = f"{color}{formatted_message}{reset}" 47 | record.args = () # 清空参数,因为消息已经格式化 48 | 49 | # 格式化日志记录 50 | formatted = super().format(record) 51 | 52 | # 恢复原始值(避免影响其他处理器) 53 | record.levelname = original_levelname 54 | record.msg = original_msg 55 | record.args = original_args 56 | 57 | return formatted 58 | else: 59 | return super().format(record) 60 | 61 | 62 | def setup_logger( 63 | name: Optional[str] = None, 64 | level: int = logging.INFO, 65 | use_color: bool = True, 66 | format_string: Optional[str] = None 67 | ) -> logging.Logger: 68 | """ 69 | 设置并返回配置好的日志记录器 70 | 71 | Args: 72 | name: 日志记录器名称,如果为 None 则使用调用模块的名称 73 | level: 日志级别,默认为 INFO 74 | use_color: 是否使用彩色输出,默认为 True 75 | format_string: 自定义格式字符串,如果为 None 则使用默认格式 76 | 77 | Returns: 78 | 配置好的日志记录器 79 | """ 80 | # 创建日志记录器 81 | logger = logging.getLogger(name) 82 | logger.setLevel(level) 83 | 84 | # 不添加处理器,让日志传播到根日志记录器统一处理 85 | # 这样可以避免重复输出 86 | # 如果根日志记录器已配置,子日志记录器会自动使用根日志记录器的配置 87 | 88 | return logger 89 | 90 | 91 | def get_logger(name: Optional[str] = None) -> logging.Logger: 92 | """ 93 | 获取日志记录器(便捷函数) 94 | 95 | Args: 96 | name: 日志记录器名称,如果为 None 则使用调用模块的名称 97 | 98 | Returns: 99 | 日志记录器实例 100 | """ 101 | return setup_logger(name=name) 102 | 103 | 104 | # 配置根日志记录器 105 | def configure_root_logger( 106 | level: int = logging.INFO, 107 | use_color: bool = True, 108 | format_string: Optional[str] = None 109 | ) -> None: 110 | """ 111 | 配置根日志记录器 112 | 113 | Args: 114 | level: 日志级别,默认为 INFO 115 | use_color: 是否使用彩色输出,默认为 True 116 | format_string: 自定义格式字符串,如果为 None 则使用默认格式 117 | """ 118 | if format_string is None: 119 | format_string = '%(asctime)s | %(levelname)-8s | %(name)s | %(message)s' 120 | 121 | root_logger = logging.getLogger() 122 | root_logger.setLevel(level) 123 | 124 | # 清除现有的处理器和基本配置 125 | root_logger.handlers.clear() 126 | 127 | # 禁用默认的 basicConfig,避免重复配置 128 | # 通过设置 root_logger 的 propagate 为 True(默认),让所有子日志记录器传播到根日志记录器 129 | 130 | # 创建控制台处理器 131 | console_handler = logging.StreamHandler(sys.stdout) 132 | console_handler.setLevel(level) 133 | 134 | # 创建彩色格式化器 135 | formatter = ColoredFormatter( 136 | use_color=use_color, 137 | fmt=format_string, 138 | datefmt='%Y-%m-%d %H:%M:%S' 139 | ) 140 | console_handler.setFormatter(formatter) 141 | 142 | # 添加处理器到根日志记录器 143 | root_logger.addHandler(console_handler) 144 | 145 | # 设置第三方库的日志级别 146 | logging.getLogger("uvicorn").setLevel(logging.INFO) 147 | logging.getLogger("uvicorn.access").setLevel(logging.INFO) 148 | logging.getLogger("httpx").setLevel(logging.WARNING) 149 | logging.getLogger("httpcore").setLevel(logging.WARNING) 150 | 151 | -------------------------------------------------------------------------------- /app/utils/file_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | 文件处理工具函数 3 | """ 4 | import base64 5 | import requests 6 | from typing import Optional, Tuple 7 | 8 | from app.utils.logger import get_logger 9 | 10 | logger = get_logger(__name__) 11 | 12 | 13 | def guess_content_type(file_name: str, default: str = "image/jpeg") -> str: 14 | """ 15 | 根据文件名猜测 Content-Type 16 | 17 | Args: 18 | file_name: 文件名 19 | default: 默认Content-Type 20 | 21 | Returns: 22 | Content-Type字符串 23 | """ 24 | suffix = file_name.lower() 25 | if suffix.endswith((".png", ".jpeg", ".jpg", ".webp", ".gif")): 26 | if suffix.endswith(".png"): 27 | return "image/png" 28 | if suffix.endswith(".webp"): 29 | return "image/webp" 30 | if suffix.endswith(".gif"): 31 | return "image/gif" 32 | return "image/jpeg" 33 | return default 34 | 35 | 36 | 37 | def get_file_extension_from_content_type(content_type: str) -> str: 38 | """ 39 | 根据 content_type 返回对应的文件扩展名 40 | 41 | Args: 42 | content_type: MIME类型 43 | 44 | Returns: 45 | 文件扩展名(包含点号) 46 | """ 47 | content_type_map = { 48 | "image/jpeg": ".jpg", 49 | "image/jpg": ".jpg", 50 | "image/png": ".png", 51 | "image/webp": ".webp", 52 | "image/gif": ".gif", 53 | } 54 | return content_type_map.get(content_type.lower(), ".jpg") 55 | 56 | 57 | 58 | def decode_base64_img(b64_string: str, content_type: str) -> Tuple[Optional[bytes], Optional[str]]: 59 | """ 60 | 解码 base64 字符串到图片数据 61 | 62 | Args: 63 | b64_string: base64编码的字符串 64 | content_type: 图片的Content-Type 65 | 66 | Returns: 67 | (图片数据, Content-Type) 元组,失败返回 (None, None) 68 | """ 69 | try: 70 | return base64.b64decode(b64_string), content_type 71 | except Exception as e: 72 | logger.error(f"解析 base64 图片失败:{e}") 73 | return None, None 74 | 75 | 76 | 77 | def get_image_data_and_type(image_path: str, file_name: str) -> Tuple[Optional[bytes], Optional[str]]: 78 | """ 79 | 获取图片数据和 Content-Type,支持本地路径、URL 和 base64 80 | 81 | Args: 82 | image_path: 图片路径(本地路径、URL或base64字符串) 83 | file_name: 文件名(用于猜测类型) 84 | 85 | Returns: 86 | (图片数据, Content-Type) 元组,失败返回 (None, None) 87 | """ 88 | # URL 图片 89 | if image_path.startswith(("http://", "https://")): 90 | logger.info("检测到 image_path 是图片URL,正在下载...") 91 | try: 92 | img_resp = requests.get(image_path, timeout=30) 93 | img_resp.raise_for_status() 94 | content_type = img_resp.headers.get("Content-Type", guess_content_type(file_name)) 95 | if not content_type.startswith("image/"): 96 | content_type = guess_content_type(file_name) 97 | return img_resp.content, content_type 98 | except requests.exceptions.RequestException as e: 99 | logger.error(f"图片URL下载失败: {e}") 100 | return None, None 101 | 102 | # data:image/xxx;base64 格式 103 | data_url_prefix = "data:image/" 104 | if image_path.strip().startswith(data_url_prefix): 105 | logger.info("检测到 image_path 是 data:image/xxx;base64 格式,正在解码...") 106 | try: 107 | head, b64data = image_path.split(",", 1) 108 | content_type = guess_content_type(file_name) 109 | if ";base64" in head: 110 | if "png" in head: 111 | content_type = "image/png" 112 | elif "webp" in head: 113 | content_type = "image/webp" 114 | elif "gif" in head: 115 | content_type = "image/gif" 116 | 117 | return decode_base64_img(b64data, content_type) 118 | logger.error("不是标准的 data:image/xxx;base64 编码") 119 | except Exception as e: 120 | logger.error(f"解析 data URL 图片失败:{e}") 121 | return None, None 122 | 123 | # 纯 base64 字符串 124 | _img_str = image_path.strip() 125 | # 简单判断是否为 base64 串(长度>128,且只包含 base64 字符) 126 | if len(_img_str) > 128 and all(c.isalnum() or c in "+/=" for c in _img_str): 127 | logger.info("检测到 image_path 可能是 base64 图片串,正在解码...") 128 | content_type = guess_content_type(file_name) 129 | return decode_base64_img(_img_str, content_type) 130 | 131 | # 本地文件路径 132 | logger.info("检测到 image_path 是本地文件,正在读取内容...") 133 | try: 134 | with open(image_path, "rb") as f: 135 | data = f.read() 136 | content_type = guess_content_type(file_name) 137 | return data, content_type 138 | except FileNotFoundError: 139 | logger.error(f"文件不存在: {image_path}") 140 | return None, None 141 | except Exception as e: 142 | logger.error(f"读取本地文件失败:{e}") 143 | return None, None 144 | 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kiira2API 2 | 3 | [![Python](https://img.shields.io/badge/Python-3.11+-blue.svg)](https://www.python.org/) 4 | [![FastAPI](https://img.shields.io/badge/FastAPI-0.121.2-green.svg)](https://fastapi.tiangolo.com/) 5 | [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) 6 | 7 | 基于 FastAPI 的 API 服务,提供兼容 OpenAI API 格式的聊天完成和模型查询接口。项目采用模块化架构,整合了 Kiira AI 客户端功能。 8 | 9 | ## ✨ 特性 10 | 11 | - 🚀 **OpenAI API 兼容**: 完全兼容 OpenAI API 格式,便于现有客户端集成 12 | - 💬 **聊天完成接口**: 支持 `/v1/chat/completions` 接口,提供流式和非流式响应 13 | - 🤖 **模型查询**: 支持 `/v1/models` 接口,查询可用模型列表 14 | - 🔄 **流式响应**: 支持 Server-Sent Events (SSE) 流式响应 15 | - 📁 **文件上传**: 支持图片、文件上传功能 16 | - ⚙️ **灵活配置**: 支持环境变量和 `.env` 文件配置 17 | - 🏗️ **模块化架构**: 清晰的分层结构,易于维护和扩展 18 | 19 | ## 📋 目录 20 | 21 | - [快速开始](#快速开始) 22 | - [配置说明](#配置说明) 23 | - [API 文档](#api-文档) 24 | - [项目结构](#项目结构) 25 | - [开发指南](#开发指南) 26 | - [许可证](#许可证) 27 | 28 | ## 🚀 快速开始 29 | 30 | ### 环境要求 31 | 32 | - Python >= 3.11 33 | - [uv](https://github.com/astral-sh/uv) 包管理器(推荐) 34 | 35 | ### 安装步骤 36 | 37 | 1. **克隆项目** 38 | 39 | ```bash 40 | git clone https://github.com/move132/kiira2api.git 41 | cd kiira2api 42 | ``` 43 | 44 | 2. **安装依赖** 45 | 46 | ```bash 47 | # 使用 uv 安装依赖(推荐) 48 | uv sync 49 | ``` 50 | 51 | 3. **配置环境变量** 52 | 53 | 首先复制 `.env.example` 到 `.env`: 54 | 55 | ```bash 56 | # Linux/macOS 57 | cp .env.example .env 58 | 59 | # Windows 60 | copy .env.example .env 61 | ``` 62 | 63 | 然后根据需要编辑 `.env` 文件(可选,也可使用环境变量): 64 | .env 默认配置可以不修改,需要添加额外的agent时添加 65 | 66 | ```env 67 | # Agent 列表 68 | AGENT_LIST=["Nano Banana Pro🔥","Nano Banana Pro 🔥👉 Try Free","Sora 2: AI Video Remixer", "Veo 3: AI Video Weaver", "Midjourney Art Studio"] 69 | ``` 70 | 71 | 4. **运行服务** 72 | 73 | ```bash 74 | # 方式 1: 使用 uv 运行(推荐) 75 | uv run python main.py 76 | 77 | # 方式 2: 使用 uvicorn 直接运行 78 | uvicorn app.main:app --reload --port 8999 79 | 80 | # 方式 3: 直接运行模块 81 | uv run python -m app.main 82 | ``` 83 | 84 | 服务启动后,访问: 85 | - API 文档: http://localhost:8999/docs 86 | - 健康检查: http://localhost:8999/health 87 | 88 | ### 使用 Docker 运行 89 | 90 | 1. **构建镜像** 91 | 92 | ```bash 93 | docker build -t kiira2api:latest . 94 | ``` 95 | 96 | 2. **运行容器** 97 | 98 | ```bash 99 | # 基本运行 100 | docker run -d -p 8999:8999 -v $(pwd)/data:/app/data --name kiira2api kiira2api:latest 101 | 102 | # 使用环境变量 103 | docker run -d -p 8999:8999 \ 104 | -e API_KEY='sk-123456' \ 105 | -e AGENT_LIST='["Dress up Game", "NanoBanana PlayLab"]' \ 106 | --name kiira2api kiira2api:latest 107 | 108 | # 挂载 .env 文件 109 | docker run -d -p 8999:8999 \ 110 | --env-file .env \ 111 | --name kiira2api kiira2api:latest 112 | ``` 113 | 114 | 3. **查看日志** 115 | 116 | ```bash 117 | docker logs -f kiira2api 118 | ``` 119 | 120 | 4. **停止和删除容器** 121 | 122 | ```bash 123 | docker stop kiira2api 124 | docker rm kiira2api 125 | ``` 126 | 127 | ### 使用 Docker Compose 运行(推荐) 128 | 129 | 1. **启动服务** 130 | 131 | ```bash 132 | # 构建并启动服务 133 | docker-compose up -d 134 | 135 | # 或者使用 docker compose(新版本) 136 | docker compose up -d 137 | ``` 138 | 139 | 2. **查看日志** 140 | 141 | ```bash 142 | # 查看日志 143 | docker-compose logs -f 144 | 145 | # 或者 146 | docker compose logs -f 147 | ``` 148 | 149 | 3. **停止服务** 150 | 151 | ```bash 152 | # 停止服务 153 | docker-compose down 154 | 155 | # 停止并删除数据卷 156 | docker-compose down -v 157 | ``` 158 | 159 | 4. **重启服务** 160 | 161 | ```bash 162 | # 重启服务 163 | docker-compose restart 164 | 165 | # 重新构建并启动 166 | docker-compose up -d --build 167 | ``` 168 | 169 | **Docker Compose 优势**: 170 | - 自动挂载 `./data` 目录到容器 171 | - 自动加载 `.env` 文件配置 172 | - 自动配置网络和健康检查 173 | - 支持服务重启策略 174 | - 更简单的管理命令 175 | 176 | ### 使用 GitHub Container 镜像 177 | 178 | 1. **拉取镜像** 179 | 180 | ```bash 181 | # 拉取最新版本 182 | docker pull ghcr.io/move132/kiira2api:latest 183 | 184 | # 拉取特定版本 185 | docker pull ghcr.io/move132/kiira2api:v1.0.0 186 | ``` 187 | 188 | 2. **运行容器** 189 | 190 | ```bash 191 | # 使用 GHCR 镜像运行 192 | docker run -d -p 8999:8999 \ 193 | -v $(pwd)/data:/app/data \ 194 | --name kiira2api \ 195 | ghcr.io/move132/kiira2api:latest 196 | ``` 197 | ## ⚙️ 配置说明 198 | 199 | ### 环境变量 200 | 201 | 所有配置项都支持通过环境变量或 `.env` 文件设置,优先级:**环境变量 > .env 文件 > 默认值** 202 | 203 | | 变量名 | 说明 | 默认值 | 204 | |--------|------|--------| 205 | | `API_KEY` | API 密钥(用于接口鉴权) | `sk-123456` | 206 | | `DEFAULT_AGENT_NAME` | 默认代理名称 | `Nano Banana Pro🔥` | 207 | | `AGENT_LIST` | Agent 列表(JSON 或逗号分隔) | `[]` | 208 | 209 | **注意**: 如果 `API_KEY` 使用默认值 `sk-123456`,系统将跳过鉴权验证。生产环境请务必修改为安全的密钥。 210 | 211 | ## 📚 API 文档 212 | 213 | ### 鉴权说明 214 | 215 | 所有 `/v1/*` 接口都需要进行 API Key 鉴权(除非使用默认值 `sk-123456`)。 216 | 217 | **鉴权方式**(两种方式任选其一): 218 | 219 | 1. **Authorization Header**(推荐,兼容 OpenAI API 格式): 220 | ```bash 221 | Authorization: Bearer sk-your-api-key-here 222 | ``` 223 | 224 | 2. **X-API-Key Header**: 225 | ```bash 226 | X-API-Key: sk-your-api-key-here 227 | ``` 228 | 229 | **示例请求**: 230 | 231 | ```bash 232 | curl -X POST "http://localhost:8999/v1/chat/completions" \ 233 | -H "Authorization: Bearer sk-your-api-key-here" \ 234 | -H "Content-Type: application/json" \ 235 | -d '{ 236 | "model": "Nano Banana Pro🔥", 237 | "messages": [{"role": "user", "content": "你好"}] 238 | }' 239 | ``` 240 | 241 | 如果 API Key 无效或缺失,将返回 `401 Unauthorized` 错误。 242 | 243 | ### POST /v1/chat/completions 244 | 245 | 聊天完成接口,兼容 OpenAI API 格式。 246 | 247 | **请求示例**: 248 | 249 | ```bash 250 | curl -X POST "http://localhost:8999/v1/chat/completions" \ 251 | -H "Authorization: Bearer sk-your-api-key-here" \ 252 | -H "Content-Type: application/json" \ 253 | -d '{ 254 | "model": "Nano Banana Pro🔥", 255 | "messages": [ 256 | {"role": "user", "content": "你好"} 257 | ], 258 | "stream": false 259 | }' 260 | ``` 261 | 262 | **流式响应示例**: 263 | 264 | ```bash 265 | curl -X POST "http://localhost:8999/v1/chat/completions" \ 266 | -H "Authorization: Bearer sk-your-api-key-here" \ 267 | -H "Content-Type: application/json" \ 268 | -d '{ 269 | "model": "Nano Banana Pro🔥", 270 | "messages": [ 271 | {"role": "user", "content": "你好"} 272 | ], 273 | "stream": true 274 | }' 275 | ``` 276 | 277 | **响应格式**: 278 | 279 | ```json 280 | { 281 | "id": "chatcmpl-xxx", 282 | "object": "chat.completion", 283 | "created": 1677652288, 284 | "choices": [ 285 | { 286 | "index": 0, 287 | "message": { 288 | "role": "assistant", 289 | "content": "你好!有什么可以帮助你的吗?" 290 | }, 291 | "finish_reason": "stop" 292 | } 293 | ], 294 | "usage": { 295 | "prompt_tokens": 9, 296 | "completion_tokens": 12, 297 | "total_tokens": 21 298 | } 299 | } 300 | ``` 301 | 302 | ### GET /v1/models 303 | 304 | 获取可用模型列表。 305 | 306 | **请求示例**: 307 | 308 | ```bash 309 | curl -X GET "http://localhost:8999/v1/models" \ 310 | -H "Authorization: Bearer sk-your-api-key-here" 311 | ``` 312 | 313 | **响应格式**: 314 | 315 | ```json 316 | { 317 | "object": "list", 318 | "data": [ 319 | { 320 | "id": "Nano Banana Pro🔥", 321 | "object": "model", 322 | "created": 1677610602, 323 | "owned_by": "move132" 324 | } 325 | ] 326 | } 327 | ``` 328 | 329 | ### GET /health 330 | 331 | 健康检查端点。 332 | 333 | **响应格式**: 334 | 335 | ```json 336 | { 337 | "status": "healthy" 338 | } 339 | ``` 340 | 341 | ## 📁 项目结构 342 | 343 | ``` 344 | kiira2api/ 345 | ├── app/ # 应用主目录 346 | │ ├── __init__.py 347 | │ ├── main.py # FastAPI 应用入口 348 | │ ├── config.py # 配置管理 349 | │ ├── models/ # 数据模型 350 | │ │ ├── __init__.py 351 | │ │ └── schemas.py # Pydantic 数据模型定义 352 | │ ├── api/ # API 路由 353 | │ │ ├── __init__.py 354 | │ │ └── v1/ # v1 版本 API 355 | │ │ ├── __init__.py 356 | │ │ ├── chat.py # 聊天相关路由 357 | │ │ └── models.py # 模型相关路由 358 | │ ├── services/ # 业务逻辑服务 359 | │ │ ├── __init__.py 360 | │ │ ├── chat_service.py # 聊天服务 361 | │ │ └── kiira_client.py # Kiira AI 客户端服务 362 | │ └── utils/ # 工具函数 363 | │ ├── __init__.py 364 | │ ├── http_client.py # HTTP 请求工具 365 | │ ├── file_utils.py # 文件处理工具 366 | │ ├── stream_parser.py # 流式响应解析工具 367 | │ └── logger.py # 日志工具 368 | ├── data/ # 数据目录 369 | │ └── account.json # 账户配置(示例) 370 | ├── main.py # 兼容入口 371 | ├── pyproject.toml # 项目配置和依赖管理 372 | ├── uv.lock # 依赖锁定文件 373 | ├── memory_bank.md # 项目记忆库 374 | └── README.md # 项目说明文档 375 | ``` 376 | 377 | ## 🛠️ 开发指南 378 | 379 | ### 技术栈 380 | 381 | - **Python**: >=3.11 382 | - **FastAPI**: 0.121.2 - 现代、快速的 Web 框架 383 | - **Uvicorn**: 0.38.0 - ASGI 服务器 384 | - **Pydantic**: 2.12.4 - 数据验证和序列化 385 | - **Requests**: 2.32.5 - HTTP 客户端库 386 | - **pydantic-settings**: 2.0.0 - 配置管理 387 | 388 | ### 代码规范 389 | 390 | - 使用类型提示(Type Hints) 391 | - 遵循 PEP 8 代码风格 392 | - 使用 Pydantic BaseModel 定义数据模型 393 | - API 路由使用 APIRouter 组织 394 | - 使用 `{{CHENGQI:...}}` 注释标记代码变更 395 | 396 | ### 架构设计 397 | 398 | - **分层架构**: API 层、服务层、工具层清晰分离 399 | - **模块化设计**: 功能按模块拆分,职责清晰 400 | - **依赖注入**: 使用 FastAPI 的依赖注入系统 401 | - **数据验证**: 使用 Pydantic 进行请求/响应数据验证 402 | 403 | ### 开发流程 404 | 405 | 1. **理解需求**: 阅读 `memory_bank.md` 了解项目上下文 406 | 2. **设计方案**: 确定技术实现方案 407 | 3. **编写代码**: 按照项目规范编写代码 408 | 4. **测试验证**: 确保功能正常 409 | 5. **更新文档**: 更新相关文档和记忆库 410 | 411 | ## 📝 更新日志 412 | 413 | ### v1.0.0 (2025-11-20) 414 | 415 | - ✨ 初始版本发布 416 | - 🎉 实现 OpenAI API 兼容接口 417 | - 🔄 支持流式响应 418 | - ⚙️ 支持环境变量配置 419 | - 📦 模块化架构重构 420 | 421 | ## 🤝 贡献 422 | 423 | 欢迎提交 Issue 和 Pull Request! 424 | 425 | ## 📄 许可证 426 | 427 | 本项目采用 MIT 许可证。详情请参阅 [LICENSE](LICENSE) 文件。 428 | 429 | ## 👤 作者 430 | 431 | **move132** 432 | 433 | - GitHub: [@move132](https://github.com/move132) 434 | - 项目地址: https://github.com/move132/kiira2api 435 | 436 | ## 🙏 致谢 437 | 438 | 感谢所有为本项目做出贡献的开发者! 439 | 440 | --- 441 | 442 | **注意**: 本项目仅供学习和研究使用。使用本服务时,请遵守相关服务的使用条款。 443 | 444 | -------------------------------------------------------------------------------- /app/services/chat_service.py: -------------------------------------------------------------------------------- 1 | """ 2 | 聊天服务:处理聊天完成业务逻辑 3 | """ 4 | import uuid 5 | import json 6 | import time 7 | import os 8 | from typing import Optional, Dict, Any, List, Iterator 9 | from fastapi import HTTPException 10 | 11 | from app.services.kiira_client import KiiraAIClient 12 | from app.utils.stream_parser import extract_media_from_data 13 | from app.config import DEFAULT_AGENT_NAME, AGENT_LIST 14 | from app.utils.logger import get_logger 15 | 16 | logger = get_logger(__name__) 17 | 18 | 19 | class ChatService: 20 | """聊天服务类""" 21 | 22 | def __init__(self, device_id: Optional[str] = None, token: Optional[str] = None, group_id: Optional[str] = None): 23 | """ 24 | 初始化聊天服务 25 | 26 | Args: 27 | device_id: 设备ID,如果未提供则自动生成 28 | token: 认证token,如果未提供则自动登录获取 29 | group_id: 群组ID,如果未提供则自动查找 30 | """ 31 | self.device_id = device_id or str(uuid.uuid4()) 32 | self.client = KiiraAIClient(device_id=self.device_id, token=token, group_id=group_id) 33 | self._initialized = False 34 | def save_account_info(self): 35 | """保存账号信息到文件(数组格式)""" 36 | try: 37 | account_info = { 38 | "user_name": self.client.user_name, 39 | "group_id": self.client.group_id, 40 | "token": self.client.token 41 | } 42 | os.makedirs("data", exist_ok=True) 43 | account_file = "data/account.json" 44 | accounts = [] 45 | if os.path.exists(account_file): 46 | with open(account_file, "r", encoding="utf-8") as f: 47 | try: 48 | accounts = json.load(f) 49 | if not isinstance(accounts, list): 50 | accounts = [] 51 | except Exception: 52 | accounts = [] 53 | # 追加当前账号信息 54 | accounts.append(account_info) 55 | with open(account_file, "w", encoding="utf-8") as f: 56 | json.dump(accounts, f, ensure_ascii=False, indent=2) 57 | 58 | except Exception as e: 59 | logger.error(f"写入账号信息失败: {e}") 60 | pass 61 | async def _ensure_initialized(self, agent_name: str = DEFAULT_AGENT_NAME): 62 | """确保客户端已初始化(登录并获取群组)""" 63 | if self._initialized: 64 | return 65 | # 如果没有token,先登录 66 | if not self.client.token: 67 | logger.info("正在获取游客Token...") 68 | if not self.client.login_guest(): 69 | raise HTTPException(status_code=500, detail="无法获取认证token") 70 | logger.info("✅ Token获取成功") 71 | # 获取当前用户信息 72 | user_info, name = self.client.get_my_info() 73 | if user_info: 74 | self.client.user_name = name 75 | # 如果没有群组ID,获取群组列表 76 | if not self.client.group_id: 77 | logger.info(f"正在获取聊天群组ID ({agent_name})...") 78 | result = self.client.get_my_chat_group_list(agent_name=agent_name) 79 | if not result: 80 | raise HTTPException( 81 | status_code=404, 82 | detail=f"无法找到指定的Agent群组: {agent_name}" 83 | ) 84 | self.save_account_info() 85 | self._initialized = True 86 | 87 | def _extract_images_from_messages(self, messages: List[Any]) -> List[Dict[str, Any]]: 88 | """ 89 | 从消息中提取图片资源 90 | 91 | Args: 92 | messages: 消息列表(可以是字典或 Pydantic 模型对象) 93 | 94 | Returns: 95 | 资源列表 96 | """ 97 | resources = [] 98 | for message in messages: 99 | # 处理 Pydantic 模型对象或字典 100 | if hasattr(message, 'content'): 101 | content = message.content 102 | elif isinstance(message, dict): 103 | content = message.get("content", "") 104 | else: 105 | continue 106 | 107 | if isinstance(content, str): 108 | # 检查是否包含图片URL 109 | if content.startswith(("http://", "https://")): 110 | # 尝试上传图片 111 | uploaded = self.client.upload_resource(content) 112 | if uploaded: 113 | resources.append({ 114 | "name": uploaded.get('name'), 115 | "size": uploaded.get('size'), 116 | "url": uploaded.get('url'), 117 | "type": "image" 118 | }) 119 | elif isinstance(content, list): 120 | # 处理多模态内容(文本+图片) 121 | for item in content: 122 | if isinstance(item, dict) and item.get("type") == "image_url": 123 | image_url = item.get("image_url", {}).get("url", "") 124 | if image_url: 125 | uploaded = self.client.upload_resource(image_url) 126 | if uploaded: 127 | resources.append({ 128 | "name": uploaded.get('name'), 129 | "size": uploaded.get('size'), 130 | "url": uploaded.get('url'), 131 | "type": "image" 132 | }) 133 | return resources 134 | 135 | def _build_prompt_from_messages(self, messages: List[Any]) -> str: 136 | """ 137 | 从消息列表构建提示词 138 | 139 | Args: 140 | messages: 消息列表(可以是字典或 Pydantic 模型对象) 141 | 142 | Returns: 143 | 组合后的提示词 144 | """ 145 | prompt_parts = [] 146 | for msg in messages: 147 | # 处理 Pydantic 模型对象或字典 148 | if hasattr(msg, 'role') and hasattr(msg, 'content'): 149 | role = msg.role 150 | content = msg.content 151 | elif isinstance(msg, dict): 152 | role = msg.get("role", "") 153 | content = msg.get("content", "") 154 | else: 155 | continue 156 | 157 | if isinstance(content, str): 158 | # 如果是字符串,直接使用(但排除纯URL,因为URL会被提取为图片) 159 | if role == "user" and not content.startswith(("http://", "https://")): 160 | prompt_parts.append(content) 161 | elif role == "assistant": 162 | # 可以添加助手回复的上下文 163 | pass 164 | elif isinstance(content, list): 165 | # 处理多模态内容(文本+图片) 166 | text_parts = [] 167 | for item in content: 168 | if isinstance(item, dict): 169 | if item.get("type") == "text": 170 | text_parts.append(item.get("text", "")) 171 | if text_parts: 172 | prompt_parts.append(" ".join(text_parts)) 173 | 174 | return "\n".join(prompt_parts) if prompt_parts else "" 175 | 176 | async def chat_completion( 177 | self, 178 | messages: List[Any], 179 | model: str = "", 180 | agent_name: str = DEFAULT_AGENT_NAME, 181 | stream: bool = False 182 | ) -> Dict[str, Any]: 183 | """ 184 | 执行聊天完成 185 | 186 | Args: 187 | messages: 消息列表 188 | model: 模型名称(暂时未使用) 189 | model_name: 模型名称 190 | stream: 是否流式返回 191 | 192 | Returns: 193 | 响应数据 194 | """ 195 | # 确保已初始化 196 | await self._ensure_initialized(model) 197 | # 构建提示词 198 | prompt = self._build_prompt_from_messages(messages) 199 | if not prompt: 200 | raise HTTPException(status_code=400, detail="消息内容不能为空") 201 | 202 | # 提取图片资源 203 | resources = self._extract_images_from_messages(messages) 204 | agent_list = self.client.get_agent_list() 205 | # 遍历 agent_list 中的每一个 agent,调用 create_chat_group 206 | if agent_list and isinstance(agent_list, list): 207 | for agent in agent_list: 208 | label = agent.get("label", "") 209 | # 只处理 label 为 AGENT_LIST 的 agent 210 | if label in AGENT_LIST: 211 | account_no = agent.get("account_no") 212 | if account_no: 213 | self.client.create_chat_group(account_no, label) 214 | # 发送消息 215 | task_id = self.client.send_message( 216 | message=prompt, 217 | at_account_no=self.client.at_account_no, 218 | resources=resources if resources else None 219 | ) 220 | 221 | if not task_id: 222 | raise HTTPException(status_code=500, detail="发送消息失败") 223 | 224 | if stream: 225 | # 流式响应在路由中处理 226 | return {"task_id": task_id, "stream": True, "group_id": self.client.group_id, "token": self.client.token} 227 | else: 228 | # 非流式响应:收集所有流式数据后返回 229 | return await self._collect_stream_response(task_id) 230 | 231 | async def _collect_stream_response(self, task_id: str) -> Dict[str, Any]: 232 | """ 233 | 收集流式响应并转换为完整响应 234 | 235 | Args: 236 | task_id: 任务ID 237 | 238 | Returns: 239 | 完整的响应数据 240 | """ 241 | full_content = "" 242 | video_url = None 243 | 244 | for line in self.client.stream_chat_completions(task_id): 245 | if line.startswith("data: "): 246 | if line[6:].strip() == "[DONE]": 247 | break 248 | 249 | try: 250 | json_data = json.loads(line[6:]) 251 | 252 | # 优化: 一次解析同时提取媒体URL和content 253 | parsed_result = extract_media_from_data(json_data) 254 | if parsed_result: 255 | video_url, _ = parsed_result 256 | break 257 | 258 | choices = json_data.get('choices', []) 259 | if choices and isinstance(choices, list) and len(choices) > 0: 260 | choice = choices[0] 261 | if isinstance(choice, dict): 262 | # 优先从 delta 中获取 content 263 | delta = choice.get('delta', {}) 264 | content = delta.get('content', '') if isinstance(delta, dict) else '' 265 | 266 | # 如果没有 delta 内容,尝试从 message 中获取 267 | if not content: 268 | message = choice.get('message', {}) 269 | content = message.get('content', '') if isinstance(message, dict) else '' 270 | 271 | if content: 272 | full_content += content 273 | except json.JSONDecodeError: 274 | pass 275 | except Exception as e: 276 | logger.debug(f"解析响应数据时出错: {e}") 277 | 278 | # 构建 OpenAI 格式的响应 279 | response_id = f"chatcmpl-{task_id}" 280 | created = int(time.time()) 281 | 282 | choices = [{ 283 | "index": 0, 284 | "message": { 285 | "role": "assistant", 286 | "content": full_content 287 | }, 288 | "finish_reason": "stop" 289 | }] 290 | 291 | # 如果有视频URL,添加到响应中 292 | if video_url: 293 | choices[0]["message"]["video_url"] = video_url 294 | 295 | return { 296 | "id": response_id, 297 | "object": "chat.completion", 298 | "created": created, 299 | "model": "sora-2", 300 | "choices": choices, 301 | "usage": { 302 | "prompt_tokens": len(full_content.split()) if full_content else 0, 303 | "completion_tokens": len(full_content.split()) if full_content else 0, 304 | "total_tokens": len(full_content.split()) * 2 if full_content else 0 305 | } 306 | } 307 | 308 | def stream_chat_completion(self, task_id: str) -> Iterator[str]: 309 | """ 310 | 流式返回聊天响应 311 | 312 | Args: 313 | task_id: 任务ID 314 | 315 | Yields: 316 | SSE格式的响应行 317 | """ 318 | for line in self.client.stream_chat_completions(task_id): 319 | yield line 320 | -------------------------------------------------------------------------------- /app/api/v1/chat.py: -------------------------------------------------------------------------------- 1 | """ 2 | 聊天相关 API 路由 3 | """ 4 | import re 5 | import json 6 | from uuid import uuid4 7 | import time 8 | from fastapi import APIRouter, HTTPException, Depends 9 | from typing import Optional 10 | from fastapi.responses import StreamingResponse 11 | from app.models.schemas import ChatCompletionRequest, ChatCompletionResponse 12 | from app.services.chat_service import ChatService 13 | from app.utils.stream_parser import extract_media_from_data 14 | from app.utils.logger import get_logger 15 | from app.api.dependencies import verify_api_key 16 | 17 | logger = get_logger(__name__) 18 | router = APIRouter(tags=["chat"]) 19 | 20 | @router.post("/chat/completions") 21 | async def chat_completions( 22 | request: ChatCompletionRequest, 23 | api_key: Optional[str] = Depends(verify_api_key) 24 | ): 25 | """ 26 | Chat completions endpoint compatible with OpenAI API format. 27 | 参考 temp.py 中的运行示例实现实际业务逻辑。 28 | """ 29 | try: 30 | # 将 Pydantic 模型转换为字典 31 | messages = [{"role": msg.role, "content": msg.content} for msg in request.messages] 32 | 33 | # Group ID: 【{group_id}】 34 | msg_group_id = None 35 | msg_token = None 36 | for msg in request.messages: 37 | # 这里改成从 assistant 内容中获取 group_id 和 token 38 | if getattr(msg, "role", None) == "assistant": 39 | content = getattr(msg, "content", "") 40 | if isinstance(content, str): 41 | match = re.search(r'\{\s*"group_id"\s*:\s*"([^"]+)"\s*,\s*"token"\s*:\s*"([^"]+)"\s*\}', content) 42 | if match: 43 | msg_group_id = match.group(1) 44 | msg_token = match.group(2) 45 | logger.info(f"获取到历史记录中的 Group ID: {msg_group_id}, Token: {msg_token[-20:]}") 46 | break 47 | # 创建聊天服务实例 48 | if not msg_group_id: 49 | logger.warning(f"未获取到历史记录中的 Group ID, 创建新的聊天服务实例") 50 | chat_service = ChatService() 51 | else: 52 | logger.info(f"获取到历史记录中的 Group ID, 直接继续原来的聊天") 53 | chat_service = ChatService(group_id=msg_group_id, token=msg_token) 54 | 55 | last_message = request.messages[-1] if request.messages else None 56 | prompt = ( 57 | last_message.content 58 | if last_message and hasattr(last_message, "content") and isinstance(last_message.content, str) 59 | else "" 60 | ) 61 | if prompt == "hi": 62 | logger.info(f"验证接口是否可用,{request.model},直接返回正常响应") 63 | return { 64 | "id": str(uuid4()), 65 | "model": request.model, 66 | "object":"chat.completion.chunk", 67 | "choices": [{ 68 | "index":0, 69 | "message":{"role": "assistant", "content": "hi"}, 70 | "finish_reason":"stop" 71 | }], 72 | "created":int(time.time()) 73 | } 74 | # 如果请求流式响应 75 | if request.stream: 76 | if msg_group_id and msg_token: 77 | resources = chat_service._extract_images_from_messages([last_message]) if last_message else [] 78 | logger.info(f"复用对话状态 group_id={msg_group_id}, 提取图片资源数量: {len(resources)}") 79 | 80 | # 直接使用已有的group_id和token 81 | chat_service.client.group_id = msg_group_id 82 | chat_service.client.token = msg_token 83 | 84 | # 如果有at_account_no缓存则使用,否则查询一次 85 | if not chat_service.client.at_account_no: 86 | _, at_account_no = chat_service.client.get_my_chat_group_list(request.model) 87 | else: 88 | at_account_no = chat_service.client.at_account_no 89 | 90 | task_id = chat_service.client.send_message( 91 | message=prompt, 92 | at_account_no=at_account_no, 93 | resources=resources if resources else None 94 | ) 95 | result = { 96 | "task_id": task_id, 97 | "group_id": msg_group_id, 98 | "token": msg_token 99 | } 100 | else: 101 | # 执行聊天完成并获取 task_id 102 | result = await chat_service.chat_completion( 103 | messages=[last_message], 104 | model=request.model, 105 | stream=True 106 | ) 107 | task_id = result.get("task_id") 108 | group_id = result.get("group_id") 109 | token = result.get("token") 110 | if not task_id: 111 | raise HTTPException(status_code=500, detail="无法获取任务ID") 112 | 113 | # 返回流式响应 114 | async def generate_stream(): 115 | """生成 OpenAI 格式的流式响应""" 116 | response_id = f"chatcmpl-{task_id}" 117 | created = int(time.time()) 118 | model = request.model 119 | media_url = None 120 | media_type = None 121 | done_sent = False 122 | if len(request.messages) == 1: 123 | # 发送group_id 124 | # content = f"""
Group ID:【{group_id}】
125 | #
Token:【{token}】
126 | # """ 127 | content = f""" 128 | ```json 129 | {{"group_id": "{group_id}", "token": "{token}"}} 130 | ``` 131 | """ 132 | group_id_chunk = { 133 | "id": response_id, 134 | "object": "chat.completion.chunk", 135 | "created": created, 136 | "model": model, 137 | "choices": [{ 138 | "index": 0, 139 | "delta": {"content": content}, 140 | "finish_reason": "stop" 141 | }] 142 | } 143 | yield f"data: {json.dumps(group_id_chunk)}\n\n" 144 | try: 145 | for line in chat_service.stream_chat_completion(task_id): 146 | if not line or not line.strip(): 147 | continue 148 | # print(line) 149 | # 处理 [DONE] 标记 150 | if line.startswith("data: ") and line[6:].strip() == "[DONE]": 151 | # 如果有视频URL,发送一个包含视频URL的最终块 152 | if media_url: 153 | final_chunk = { 154 | "id": response_id, 155 | "object": "chat.completion.chunk", 156 | "created": created, 157 | "model": model, 158 | "choices": [{ 159 | "index": 0, 160 | "delta": { 161 | "content": 162 | f"\n\n![Generated Image]({media_url})\n\n" if media_type == "image" 163 | else ( 164 | f"生成视频完成.\n[点击下载视频]({media_url})" if media_type == "video" 165 | else f"\n\n{media_url}\n\n" 166 | ) 167 | }, 168 | "finish_reason": "stop" 169 | }] 170 | } 171 | yield f"data: {json.dumps(final_chunk)}\n\n" 172 | else: 173 | # 发送一个带有 finish_reason 的最终块 174 | final_chunk = { 175 | "id": response_id, 176 | "object": "chat.completion.chunk", 177 | "created": created, 178 | "model": model, 179 | "choices": [{ 180 | "index": 0, 181 | "delta": {}, 182 | "finish_reason": "stop" 183 | }] 184 | } 185 | yield f"data: {json.dumps(final_chunk)}\n\n" 186 | 187 | # 发送结束标记 188 | yield "data: [DONE]\n\n" 189 | done_sent = True 190 | break 191 | 192 | # 解析 SSE 格式的数据 193 | if line.startswith("data: "): 194 | json_str = line[6:].strip() 195 | if not json_str or json_str == "[DONE]": 196 | continue 197 | 198 | try: 199 | data = json.loads(json_str) 200 | # logger.info(f"解析数据: {data.get('choices', [])}") 201 | # 优化: 一次解析同时提取媒体URL和content,避免重复JSON解析 202 | parse_result = extract_media_from_data(data) 203 | if parse_result: 204 | parsed_media_url, parsed_media_type = parse_result 205 | media_type = parsed_media_type 206 | media_url = parsed_media_url 207 | logger.debug(f"检测到媒体资源: {media_type} - {media_url[:50]}...") 208 | 209 | # 提取 content 210 | content = "" 211 | choices_data = data.get('choices', []) 212 | if choices_data and isinstance(choices_data, list) and len(choices_data) > 0: 213 | choice = choices_data[0] 214 | if isinstance(choice, dict): 215 | # 优先从 delta 中获取 content 216 | delta = choice.get('delta', {}) 217 | if isinstance(delta, dict): 218 | content = delta.get('content', '') 219 | 220 | # 如果没有 delta 内容,尝试从 message 中获取 221 | if not content: 222 | message = choice.get('message', {}) 223 | if isinstance(message, dict): 224 | content = message.get('content', '') 225 | # 构建 OpenAI 格式的流式响应块 226 | if content: 227 | chunk = { 228 | "id": response_id, 229 | "object": "chat.completion.chunk", 230 | "created": created, 231 | "model": model, 232 | "choices": [{ 233 | "index": 0, 234 | "delta": {"content": content}, 235 | "finish_reason": None 236 | }] 237 | } 238 | yield f"data: {json.dumps(chunk)}\n\n" 239 | 240 | except json.JSONDecodeError: 241 | # 如果不是有效的 JSON,跳过 242 | logger.debug(f"跳过无效的 JSON 行: {line[:100]}") 243 | continue 244 | except Exception as e: 245 | logger.debug(f"解析流式数据时出错: {e}") 246 | continue 247 | 248 | # 如果循环正常结束(没有遇到 [DONE]),发送结束标记 249 | if not done_sent: 250 | # 发送一个带有 finish_reason 的最终块 251 | final_chunk = { 252 | "id": response_id, 253 | "object": "chat.completion.chunk", 254 | "created": created, 255 | "model": model, 256 | "choices": [{ 257 | "index": 0, 258 | "delta": {}, 259 | "finish_reason": "stop" 260 | }] 261 | } 262 | yield f"data: {json.dumps(final_chunk)}\n\n" 263 | yield "data: [DONE]\n\n" 264 | 265 | except Exception as e: 266 | # 发送错误信息 267 | logger.error(f"流式响应错误: {e}", exc_info=True) 268 | error_data = json.dumps({ 269 | "error": { 270 | "message": str(e), 271 | "type": "server_error" 272 | } 273 | }) 274 | yield f"data: {error_data}\n\n" 275 | 276 | return StreamingResponse( 277 | generate_stream(), 278 | media_type="text/event-stream", 279 | headers={ 280 | "Cache-Control": "no-cache", 281 | "Connection": "keep-alive", 282 | } 283 | ) 284 | else: 285 | # 非流式响应 286 | response_data = await chat_service.chat_completion( 287 | messages=messages, 288 | model=request.model, 289 | stream=False 290 | ) 291 | 292 | # 转换为 Pydantic 模型 293 | return ChatCompletionResponse(**response_data) 294 | 295 | except HTTPException: 296 | raise 297 | except Exception as e: 298 | logger.error(f"聊天完成失败: {e}", exc_info=True) 299 | raise HTTPException(status_code=500, detail=f"内部服务器错误: {str(e)}") -------------------------------------------------------------------------------- /app/services/kiira_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Kiira AI 客户端服务 3 | """ 4 | import uuid 5 | import time 6 | import requests 7 | from pathlib import Path 8 | from typing import Optional, Dict, Any, Iterator, List 9 | from dataclasses import dataclass 10 | 11 | from app.config import ( 12 | BASE_URL_KIIRA, 13 | BASE_URL_SEAART_API, 14 | BASE_URL_SEAART_UPLOADER, 15 | DEFAULT_AGENT_NAME 16 | ) 17 | from app.utils.http_client import build_headers, make_request, get_session 18 | from app.utils.file_utils import ( 19 | get_image_data_and_type, 20 | get_file_extension_from_content_type 21 | ) 22 | from app.utils.logger import get_logger 23 | 24 | logger = get_logger(__name__) 25 | 26 | @dataclass 27 | class KiiraAIClient: 28 | """Kiira AI API 客户端类""" 29 | 30 | device_id: str 31 | token: Optional[str] = None 32 | group_id: Optional[str] = None 33 | at_account_no: Optional[str] = None 34 | user_name: Optional[str] = None 35 | 36 | def __post_init__(self): 37 | """初始化后自动生成设备ID(如果未提供)""" 38 | if not self.device_id: 39 | self.device_id = str(uuid.uuid4()) 40 | 41 | def login_guest(self) -> Optional[str]: 42 | """游客登录,获取 token""" 43 | url = f'{BASE_URL_SEAART_API}/api/v1/login-guest' 44 | headers = build_headers( 45 | device_id=self.device_id, 46 | token='', 47 | referer=f'{BASE_URL_KIIRA}/', 48 | sec_fetch_site='cross-site' 49 | ) 50 | 51 | response_data = make_request('POST', url, device_id=self.device_id, headers=headers, json_data={}) 52 | if response_data and 'data' in response_data and 'token' in response_data['data']: 53 | self.token = response_data['data']['token'] 54 | logger.info(f"获取到游客Token: {self.token[:20]}...") 55 | return self.token 56 | else: 57 | logger.error("登录失败:未获取到token") 58 | return None 59 | 60 | def get_my_info(self) -> Optional[tuple[Dict[str, Any], str]]: 61 | """获取当前用户信息""" 62 | url = f'{BASE_URL_KIIRA}/api/v1/my' 63 | headers = build_headers(device_id=self.device_id, token=self.token, referer=f'{BASE_URL_KIIRA}/chat') 64 | 65 | response_data = make_request('POST', url, device_id=self.device_id, token=self.token, headers=headers, json_data={}) 66 | if response_data and 'data' in response_data: 67 | name = response_data.get('data', {}).get('name', '') 68 | logger.info(f"当前用户信息:{name}") 69 | return response_data, name 70 | return None, None 71 | 72 | def get_my_chat_group_list(self, agent_name: str = DEFAULT_AGENT_NAME) -> Optional[tuple[str, str]]: 73 | """获取当前账户的聊天群组列表,查找指定昵称的群组""" 74 | url = f'{BASE_URL_KIIRA}/api/v1/my-chat-group-list' 75 | headers = build_headers(device_id=self.device_id, token=self.token, accept_language='eh') 76 | data = {"page": 1, "page_size": 999} 77 | response_data = make_request('POST', url, device_id=self.device_id, token=self.token, headers=headers, json_data=data) 78 | 79 | # logger.info(f"获取当前账户的聊天群组列表,查找指定昵称的群组,响应数据: {response_data}") 80 | if not response_data or 'data' not in response_data: 81 | logger.warning("未在响应中找到群组数据") 82 | return None 83 | 84 | items = response_data.get('data', {}).get('items', []) 85 | for item in items: 86 | user_list = item.get('user_list', []) 87 | for user in user_list: 88 | if user.get('nickname') == agent_name: 89 | group_id = item.get('id') 90 | at_account_no = user.get('account_no') 91 | logger.info(f"✅ 找到群组ID: {group_id}, at_account_no: {at_account_no}") 92 | self.group_id = group_id 93 | self.at_account_no = at_account_no 94 | return group_id, at_account_no 95 | # 如果未找到,则获取 agent 列表,并创建聊天群组 96 | logger.warning(f"未在 user_list 中找到 '{agent_name}',正在尝试在agent 列表中获取 '{agent_name}',并创建聊天群组") 97 | agent_list = self.get_agent_list() 98 | # 遍历 agent_list 中的每一个 agent,调用 create_chat_group 99 | if agent_list and isinstance(agent_list, list): 100 | for agent in agent_list: 101 | label = agent.get("label", "") 102 | if label in [agent_name]: 103 | account_no_base = agent.get("account_no") 104 | if account_no_base: 105 | group_info = self.create_chat_group([account_no_base], label) 106 | group_id = group_info.get("id") 107 | at_account_no = group_info.get("user_list", [])[0].get("account_no") 108 | self.group_id = group_id 109 | self.at_account_no = at_account_no 110 | return group_id, at_account_no 111 | # 发送消息 112 | logger.warning(f"未找到 '{agent_name}'") 113 | return None, None 114 | 115 | def get_agent_list(self, category_ids: list[str] = [], keyword: str = "") -> Optional[Dict[str, Any]]: 116 | """ 117 | 获取所有 agent(代理) 列表 118 | 119 | Args: 120 | category_ids (list[str], optional): 分类ID列表,默认 [] 121 | keyword (str, optional): 搜索关键词, 默认空字符串 122 | 123 | Returns: 124 | Optional[Dict[str, Any]]: 响应数据 125 | """ 126 | url = f"{BASE_URL_KIIRA}/api/v1/agent-list" 127 | headers = build_headers( 128 | device_id=self.device_id, 129 | token=self.token, 130 | accept_language='zh,zh-CN;q=0.9,en;q=0.8,ja;q=0.7', 131 | referer=f'{BASE_URL_KIIRA}/search' 132 | ) 133 | # curl 的 body 是 {"category_ids":[],"keyword":""} 134 | data = { 135 | "category_ids": category_ids, 136 | "keyword": keyword 137 | } 138 | response_data = make_request( 139 | 'POST', 140 | url, 141 | device_id=self.device_id, 142 | token=self.token, 143 | headers=headers, 144 | json_data=data 145 | ) 146 | if response_data and 'data' in response_data: 147 | # 只返回指定字段 148 | items = response_data['data']['items'] 149 | filtered_items = [] 150 | for item in items: 151 | filtered_items.append({ 152 | "id": item.get("id"), 153 | "label": item.get("label"), 154 | "account_no": item.get("account_no"), 155 | "description": item.get("description"), 156 | }) 157 | # logger.info(f"获取 agent 列表成功,响应: {filtered_items}") 158 | return filtered_items 159 | logger.error(f"获取 agent 列表失败,响应: {response_data}") 160 | return None 161 | 162 | def create_chat_group(self, agent_account_nos: list[str], label: str) -> Optional[Dict[str, Any]]: 163 | """ 164 | 创建新的聊天群组 165 | 166 | Args: 167 | agent_account_nos (list[str]): 代理账户编号列表 168 | 169 | Returns: 170 | Optional[Dict[str, Any]]: 响应数据 171 | """ 172 | url = f"{BASE_URL_KIIRA}/api/v1/create-chat-group" 173 | headers = build_headers( 174 | device_id=self.device_id, 175 | token=self.token, 176 | accept_language='zh,zh-CN;q=0.9,en;q=0.8,ja;q=0.7', 177 | referer=f'{BASE_URL_KIIRA}/search' 178 | ) 179 | 180 | data = { 181 | "agent_account_nos": agent_account_nos 182 | } 183 | 184 | response_data = make_request( 185 | 'POST', 186 | url, 187 | device_id=self.device_id, 188 | token=self.token, 189 | headers=headers, 190 | json_data=data 191 | ) 192 | if response_data and 'data' in response_data: 193 | logger.info(f"✅添加聊天群组{label}成功") 194 | return response_data['data'] 195 | logger.error(f"添加聊天群组{label}失败,响应: {response_data}") 196 | return None 197 | 198 | def _get_upload_presign( 199 | self, 200 | resource_id: str, 201 | file_name: str, 202 | file_size: int, 203 | category: int = 74, 204 | content_type: str = 'image/jpeg' 205 | ) -> Optional[Dict[str, Any]]: 206 | """获取图片上传的 presign 信息(内部方法)""" 207 | url = f"{BASE_URL_SEAART_UPLOADER}/api/upload/pre-sign" 208 | headers = build_headers( 209 | device_id=self.device_id, 210 | token=self.token, 211 | referer=f'{BASE_URL_KIIRA}/', 212 | accept_language='zh', 213 | sec_fetch_site='cross-site' 214 | ) 215 | 216 | data = { 217 | "id": resource_id, 218 | "category": category, 219 | "content_type": content_type, 220 | "file_name": file_name, 221 | "file_size": file_size, 222 | "name": file_name, 223 | "size": file_size 224 | } 225 | 226 | return make_request('POST', url, device_id=self.device_id, token=self.token, headers=headers, json_data=data) 227 | 228 | def _upload_complete(self, resource_id: str) -> Optional[Dict[str, Any]]: 229 | """通知 seaart.dev 上传已完成(内部方法)""" 230 | url = f"{BASE_URL_SEAART_UPLOADER}/api/upload/complete" 231 | headers = build_headers( 232 | device_id=self.device_id, 233 | token=self.token, 234 | referer=f'{BASE_URL_KIIRA}/', 235 | accept_language='zh', 236 | sec_fetch_site='cross-site' 237 | ) 238 | 239 | data = {"id": resource_id} 240 | return make_request('POST', url, device_id=self.device_id, token=self.token, headers=headers, json_data=data) 241 | 242 | def upload_resource(self, image_path: str) -> Optional[Dict[str, Any]]: 243 | """ 244 | 上传文件资源,自动处理预签名、上传和完成步骤。 245 | 支持本地文件、URL 及 base64 字符串。 246 | 247 | Args: 248 | image_path: 本地文件路径、图片 URL 或 base64 字符串。 249 | 250 | Returns: 251 | 包含 'name', 'size', 'url', 'path' 的字典,失败返回 None。 252 | """ 253 | # 1. 解析文件数据和类型 254 | initial_file_name = Path(image_path).name if not (image_path.startswith("http") or image_path.startswith("data:")) else "upload.jpg" 255 | put_data, content_type = get_image_data_and_type(image_path, initial_file_name) 256 | 257 | if not put_data or not content_type: 258 | logger.error("无法获取图片数据和类型,上传失败") 259 | return None 260 | 261 | file_size = len(put_data) 262 | # 根据实际 content_type 调整 file_name 扩展名 263 | base_name = Path(initial_file_name).stem 264 | file_name = base_name + get_file_extension_from_content_type(content_type) 265 | resource_id = str(int(time.time() * 1000)) # 毫秒时间戳作为 resource_id 266 | 267 | # 2. 请求预签名 URL 268 | logger.debug(f"Step 1: 正在请求预签名 URL (Size: {file_size} bytes, Type: {content_type})...") 269 | presign_response = self._get_upload_presign( 270 | resource_id=resource_id, 271 | file_name=file_name, 272 | file_size=file_size, 273 | content_type=content_type 274 | ) 275 | 276 | if not presign_response or 'data' not in presign_response: 277 | logger.error("预签名请求失败或响应格式错误") 278 | return None 279 | 280 | presign_data = presign_response.get("data", {}) 281 | pre_signs = presign_data.get("pre_signs", []) 282 | resource_ret_id = presign_data.get("id") 283 | upload_url = (pre_signs[0].get("url") if pre_signs and isinstance(pre_signs, list) else None) 284 | 285 | if not upload_url: 286 | logger.error(f"没有拿到预签名 URL,响应: {presign_response}") 287 | return None 288 | 289 | logger.debug(f"✅ 预签名响应成功, 资源ID {resource_ret_id}") 290 | 291 | # 3. 直传图片到 GCS 292 | logger.debug("Step 2: 正在直传图片到 GCS...") 293 | 294 | # 检查预签名响应中是否有指定的 headers 295 | presign_headers = {} 296 | if pre_signs and isinstance(pre_signs, list) and len(pre_signs) > 0 and "headers" in pre_signs[0]: 297 | presign_headers.update(pre_signs[0]["headers"]) 298 | 299 | # 实际上传 headers - 使用最少的必要 headers,避免干扰 GCS 签名验证 300 | upload_headers = { 301 | "Content-Type": content_type, 302 | "Content-Length": str(file_size), 303 | } 304 | upload_headers.update(presign_headers) # 合并预签名指定的 headers 305 | 306 | try: 307 | session = get_session() 308 | put_resp = session.put( 309 | upload_url, 310 | headers=upload_headers, 311 | data=put_data, 312 | timeout=60 313 | ) 314 | if put_resp.status_code == 200: 315 | logger.info("✅ 上传成功!") 316 | else: 317 | logger.error(f"上传失败,状态码:{put_resp.status_code},响应:{put_resp.text[:200]}") 318 | return None 319 | except Exception as e: 320 | logger.error(f"上传失败:{e}") 321 | return None 322 | 323 | # 4. 调用 complete 接口获取最终图片地址 324 | logger.debug(f"Step 3: 正在调用 complete 接口获取最终图片地址...") 325 | complete_data = self._upload_complete(resource_ret_id) 326 | 327 | if complete_data and complete_data.get("status", {}).get("code") == 10000: 328 | image_data = complete_data.get("data", {}) 329 | image_path_ret = image_data.get("path") 330 | image_url = image_data.get("url") 331 | if image_url: 332 | logger.info(f"✅ 资源上传成功: {file_name} ({file_size} bytes)") 333 | return {"name": file_name, "size": file_size, "url": image_url, "path": image_path_ret, "id": resource_id} 334 | else: 335 | logger.warning("⚠️ 未在响应中找到图片URL") 336 | else: 337 | logger.error(f"⚠️ Complete 接口返回错误: {complete_data}") 338 | 339 | return None 340 | 341 | def send_message( 342 | self, 343 | message: str, 344 | at_account_no: str = 'seagen_sora2_agent', 345 | agent_type: str = "agent", 346 | at_account_no_type: str = "bot", 347 | resources: Optional[List[Dict[str, Any]]] = None, 348 | message_id: Optional[str] = None 349 | ) -> Optional[str]: 350 | """向群组发送消息""" 351 | if not self.group_id: 352 | logger.error("未设置群组ID,请先调用 get_my_chat_group_list()") 353 | return None 354 | 355 | url = f'{BASE_URL_KIIRA}/api/v1/send-message' 356 | headers = build_headers(device_id=self.device_id, token=self.token, accept_language='zh') 357 | 358 | if resources is None: 359 | resources = [] 360 | 361 | if message_id is None: 362 | # 使用 uuid1().int 的前17位作为消息ID 363 | message_id = str(uuid.uuid1().int)[:17] 364 | 365 | data = { 366 | "id": message_id, 367 | "at_account_no": at_account_no, 368 | "at_account_no_type": at_account_no_type, 369 | "resources": resources, 370 | "group_id": self.group_id, 371 | "message": message, 372 | "agent_type": agent_type 373 | } 374 | logger.info(f"发送消息: {data}") 375 | response_data = make_request('POST', url, device_id=self.device_id, token=self.token, headers=headers, json_data=data) 376 | if response_data and 'data' in response_data: 377 | task_id = response_data['data'].get('task_id') 378 | if task_id: 379 | logger.info(f"消息发送成功,task_id: {task_id}") 380 | return task_id 381 | 382 | logger.error("发送消息失败:未获取到task_id") 383 | return None 384 | 385 | def stream_chat_completions( 386 | self, 387 | task_id: str, 388 | timeout: int = 180 389 | ) -> Iterator[str]: 390 | """实时流式获取AI聊天响应""" 391 | url = f'{BASE_URL_KIIRA}/api/v1/stream/chat/completions' 392 | headers = build_headers( 393 | device_id=self.device_id, 394 | token=self.token, 395 | accept='text/event-stream', 396 | accept_language='zh' 397 | ) 398 | 399 | data = {"message_id": task_id} 400 | 401 | try: 402 | logger.info(f"开始请求流式响应,task_id: {task_id}") 403 | session = get_session() 404 | response = session.post( 405 | url, 406 | headers=headers, 407 | json=data, 408 | cookies={}, 409 | stream=True, 410 | timeout=timeout 411 | ) 412 | 413 | logger.debug(f"收到响应,状态码: {response.status_code}") 414 | if response.status_code != 200: 415 | logger.error(f"流式响应状态码错误: {response.status_code}") 416 | logger.error(f"响应内容: {response.text[:500]}") 417 | return 418 | 419 | response.encoding = 'utf-8' 420 | logger.debug("开始接收流式数据...") 421 | 422 | line_count = 0 423 | has_data = False 424 | for line in response.iter_lines(decode_unicode=True): 425 | line_count += 1 426 | if line: 427 | has_data = True 428 | if line_count == 1: 429 | logger.debug("✅ 收到第一行数据") 430 | 431 | if isinstance(line, bytes): 432 | line = line.decode('utf-8') 433 | 434 | # 跳过注释行和空行 435 | if not line.startswith(":"): 436 | yield line 437 | elif line_count == 1: 438 | logger.warning("⚠ 第一行是空行,继续等待...") 439 | 440 | 441 | if not has_data: 442 | logger.warning("⚠ 警告:没有收到任何数据") 443 | else: 444 | logger.debug(f"✅ 流式响应接收完成,共处理 {line_count} 行") 445 | 446 | except requests.exceptions.RequestException as e: 447 | logger.error(f"stream_chat_completions 网络错误: {e}") 448 | except Exception as e: 449 | logger.error(f"stream_chat_completions 错误: {e}", exc_info=True) 450 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 2 3 | requires-python = ">=3.11" 4 | 5 | [[package]] 6 | name = "annotated-doc" 7 | version = "0.0.4" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, 12 | ] 13 | 14 | [[package]] 15 | name = "annotated-types" 16 | version = "0.7.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 19 | wheels = [ 20 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, 21 | ] 22 | 23 | [[package]] 24 | name = "anyio" 25 | version = "4.11.0" 26 | source = { registry = "https://pypi.org/simple" } 27 | dependencies = [ 28 | { name = "idna" }, 29 | { name = "sniffio" }, 30 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 31 | ] 32 | sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, 35 | ] 36 | 37 | [[package]] 38 | name = "certifi" 39 | version = "2025.11.12" 40 | source = { registry = "https://pypi.org/simple" } 41 | sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } 42 | wheels = [ 43 | { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, 44 | ] 45 | 46 | [[package]] 47 | name = "charset-normalizer" 48 | version = "3.4.4" 49 | source = { registry = "https://pypi.org/simple" } 50 | sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } 51 | wheels = [ 52 | { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, 53 | { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, 54 | { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, 55 | { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, 56 | { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, 57 | { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, 58 | { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, 59 | { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, 60 | { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, 61 | { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, 62 | { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, 63 | { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, 64 | { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, 65 | { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, 66 | { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, 67 | { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, 68 | { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, 69 | { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, 70 | { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, 71 | { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, 72 | { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, 73 | { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, 74 | { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, 75 | { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, 76 | { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, 77 | { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, 78 | { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, 79 | { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, 80 | { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, 81 | { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, 82 | { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, 83 | { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, 84 | { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, 85 | { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, 86 | { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, 87 | { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, 88 | { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, 89 | { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, 90 | { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, 91 | { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, 92 | { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, 93 | { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, 94 | { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, 95 | { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, 96 | { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, 97 | { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, 98 | { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, 99 | { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, 100 | { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, 101 | { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, 102 | { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, 103 | { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, 104 | { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, 105 | { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, 106 | { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, 107 | { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, 108 | { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, 109 | { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, 110 | { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, 111 | { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, 112 | { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, 113 | { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, 114 | { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, 115 | { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, 116 | { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, 117 | ] 118 | 119 | [[package]] 120 | name = "click" 121 | version = "8.3.1" 122 | source = { registry = "https://pypi.org/simple" } 123 | dependencies = [ 124 | { name = "colorama", marker = "sys_platform == 'win32'" }, 125 | ] 126 | sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } 127 | wheels = [ 128 | { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, 129 | ] 130 | 131 | [[package]] 132 | name = "colorama" 133 | version = "0.4.6" 134 | source = { registry = "https://pypi.org/simple" } 135 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 136 | wheels = [ 137 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 138 | ] 139 | 140 | [[package]] 141 | name = "fastapi" 142 | version = "0.121.2" 143 | source = { registry = "https://pypi.org/simple" } 144 | dependencies = [ 145 | { name = "annotated-doc" }, 146 | { name = "pydantic" }, 147 | { name = "starlette" }, 148 | { name = "typing-extensions" }, 149 | ] 150 | sdist = { url = "https://files.pythonhosted.org/packages/fb/48/f08f264da34cf160db82c62ffb335e838b1fc16cbcc905f474c7d4c815db/fastapi-0.121.2.tar.gz", hash = "sha256:ca8e932b2b823ec1721c641e3669472c855ad9564a2854c9899d904c2848b8b9", size = 342944, upload-time = "2025-11-13T17:05:54.692Z" } 151 | wheels = [ 152 | { url = "https://files.pythonhosted.org/packages/eb/23/dfb161e91db7c92727db505dc72a384ee79681fe0603f706f9f9f52c2901/fastapi-0.121.2-py3-none-any.whl", hash = "sha256:f2d80b49a86a846b70cc3a03eb5ea6ad2939298bf6a7fe377aa9cd3dd079d358", size = 109201, upload-time = "2025-11-13T17:05:52.718Z" }, 153 | ] 154 | 155 | [[package]] 156 | name = "h11" 157 | version = "0.16.0" 158 | source = { registry = "https://pypi.org/simple" } 159 | sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } 160 | wheels = [ 161 | { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, 162 | ] 163 | 164 | [[package]] 165 | name = "httptools" 166 | version = "0.7.1" 167 | source = { registry = "https://pypi.org/simple" } 168 | sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } 169 | wheels = [ 170 | { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" }, 171 | { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" }, 172 | { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" }, 173 | { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" }, 174 | { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" }, 175 | { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" }, 176 | { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" }, 177 | { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, 178 | { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, 179 | { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, 180 | { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, 181 | { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, 182 | { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, 183 | { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, 184 | { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, 185 | { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, 186 | { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, 187 | { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, 188 | { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, 189 | { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, 190 | { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, 191 | { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, 192 | { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, 193 | { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, 194 | { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, 195 | { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, 196 | { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, 197 | { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, 198 | ] 199 | 200 | [[package]] 201 | name = "idna" 202 | version = "3.11" 203 | source = { registry = "https://pypi.org/simple" } 204 | sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } 205 | wheels = [ 206 | { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, 207 | ] 208 | 209 | [[package]] 210 | name = "kiira2api" 211 | version = "0.1.0" 212 | source = { virtual = "." } 213 | dependencies = [ 214 | { name = "fastapi" }, 215 | { name = "pydantic-settings" }, 216 | { name = "requests" }, 217 | { name = "uvicorn", extra = ["standard"] }, 218 | ] 219 | 220 | [package.metadata] 221 | requires-dist = [ 222 | { name = "fastapi", specifier = ">=0.121.2" }, 223 | { name = "pydantic-settings", specifier = ">=2.0.0" }, 224 | { name = "requests", specifier = ">=2.32.5" }, 225 | { name = "uvicorn", extras = ["standard"], specifier = ">=0.38.0" }, 226 | ] 227 | 228 | [[package]] 229 | name = "pydantic" 230 | version = "2.12.4" 231 | source = { registry = "https://pypi.org/simple" } 232 | dependencies = [ 233 | { name = "annotated-types" }, 234 | { name = "pydantic-core" }, 235 | { name = "typing-extensions" }, 236 | { name = "typing-inspection" }, 237 | ] 238 | sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } 239 | wheels = [ 240 | { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, 241 | ] 242 | 243 | [[package]] 244 | name = "pydantic-core" 245 | version = "2.41.5" 246 | source = { registry = "https://pypi.org/simple" } 247 | dependencies = [ 248 | { name = "typing-extensions" }, 249 | ] 250 | sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } 251 | wheels = [ 252 | { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, 253 | { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, 254 | { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, 255 | { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, 256 | { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, 257 | { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, 258 | { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, 259 | { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, 260 | { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, 261 | { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, 262 | { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, 263 | { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, 264 | { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, 265 | { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, 266 | { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, 267 | { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, 268 | { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, 269 | { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, 270 | { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, 271 | { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, 272 | { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, 273 | { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, 274 | { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, 275 | { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, 276 | { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, 277 | { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, 278 | { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, 279 | { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, 280 | { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, 281 | { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, 282 | { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, 283 | { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, 284 | { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, 285 | { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, 286 | { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, 287 | { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, 288 | { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, 289 | { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, 290 | { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, 291 | { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, 292 | { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, 293 | { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, 294 | { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, 295 | { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, 296 | { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, 297 | { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, 298 | { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, 299 | { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, 300 | { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, 301 | { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, 302 | { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, 303 | { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, 304 | { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, 305 | { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, 306 | { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, 307 | { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, 308 | { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, 309 | { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, 310 | { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, 311 | { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, 312 | { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, 313 | { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, 314 | { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, 315 | { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, 316 | { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, 317 | { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, 318 | { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, 319 | { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, 320 | { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, 321 | { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, 322 | { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, 323 | { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, 324 | { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, 325 | { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, 326 | { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, 327 | { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, 328 | { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, 329 | { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, 330 | { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, 331 | { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, 332 | { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, 333 | { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, 334 | { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, 335 | { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, 336 | { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, 337 | { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, 338 | ] 339 | 340 | [[package]] 341 | name = "pydantic-settings" 342 | version = "2.12.0" 343 | source = { registry = "https://pypi.org/simple" } 344 | dependencies = [ 345 | { name = "pydantic" }, 346 | { name = "python-dotenv" }, 347 | { name = "typing-inspection" }, 348 | ] 349 | sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } 350 | wheels = [ 351 | { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, 352 | ] 353 | 354 | [[package]] 355 | name = "python-dotenv" 356 | version = "1.2.1" 357 | source = { registry = "https://pypi.org/simple" } 358 | sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } 359 | wheels = [ 360 | { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, 361 | ] 362 | 363 | [[package]] 364 | name = "pyyaml" 365 | version = "6.0.3" 366 | source = { registry = "https://pypi.org/simple" } 367 | sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } 368 | wheels = [ 369 | { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, 370 | { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, 371 | { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, 372 | { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, 373 | { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, 374 | { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, 375 | { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, 376 | { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, 377 | { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, 378 | { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, 379 | { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, 380 | { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, 381 | { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, 382 | { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, 383 | { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, 384 | { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, 385 | { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, 386 | { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, 387 | { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, 388 | { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, 389 | { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, 390 | { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, 391 | { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, 392 | { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, 393 | { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, 394 | { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, 395 | { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, 396 | { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, 397 | { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, 398 | { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, 399 | { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, 400 | { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, 401 | { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, 402 | { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, 403 | { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, 404 | { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, 405 | { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, 406 | { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, 407 | { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, 408 | { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, 409 | { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, 410 | { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, 411 | { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, 412 | { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, 413 | { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, 414 | { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, 415 | { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, 416 | ] 417 | 418 | [[package]] 419 | name = "requests" 420 | version = "2.32.5" 421 | source = { registry = "https://pypi.org/simple" } 422 | dependencies = [ 423 | { name = "certifi" }, 424 | { name = "charset-normalizer" }, 425 | { name = "idna" }, 426 | { name = "urllib3" }, 427 | ] 428 | sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } 429 | wheels = [ 430 | { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, 431 | ] 432 | 433 | [[package]] 434 | name = "sniffio" 435 | version = "1.3.1" 436 | source = { registry = "https://pypi.org/simple" } 437 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } 438 | wheels = [ 439 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, 440 | ] 441 | 442 | [[package]] 443 | name = "starlette" 444 | version = "0.49.3" 445 | source = { registry = "https://pypi.org/simple" } 446 | dependencies = [ 447 | { name = "anyio" }, 448 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 449 | ] 450 | sdist = { url = "https://files.pythonhosted.org/packages/de/1a/608df0b10b53b0beb96a37854ee05864d182ddd4b1156a22f1ad3860425a/starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284", size = 2655031, upload-time = "2025-11-01T15:12:26.13Z" } 451 | wheels = [ 452 | { url = "https://files.pythonhosted.org/packages/a3/e0/021c772d6a662f43b63044ab481dc6ac7592447605b5b35a957785363122/starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f", size = 74340, upload-time = "2025-11-01T15:12:24.387Z" }, 453 | ] 454 | 455 | [[package]] 456 | name = "typing-extensions" 457 | version = "4.15.0" 458 | source = { registry = "https://pypi.org/simple" } 459 | sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } 460 | wheels = [ 461 | { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, 462 | ] 463 | 464 | [[package]] 465 | name = "typing-inspection" 466 | version = "0.4.2" 467 | source = { registry = "https://pypi.org/simple" } 468 | dependencies = [ 469 | { name = "typing-extensions" }, 470 | ] 471 | sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } 472 | wheels = [ 473 | { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, 474 | ] 475 | 476 | [[package]] 477 | name = "urllib3" 478 | version = "2.5.0" 479 | source = { registry = "https://pypi.org/simple" } 480 | sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } 481 | wheels = [ 482 | { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, 483 | ] 484 | 485 | [[package]] 486 | name = "uvicorn" 487 | version = "0.38.0" 488 | source = { registry = "https://pypi.org/simple" } 489 | dependencies = [ 490 | { name = "click" }, 491 | { name = "h11" }, 492 | ] 493 | sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } 494 | wheels = [ 495 | { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, 496 | ] 497 | 498 | [package.optional-dependencies] 499 | standard = [ 500 | { name = "colorama", marker = "sys_platform == 'win32'" }, 501 | { name = "httptools" }, 502 | { name = "python-dotenv" }, 503 | { name = "pyyaml" }, 504 | { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, 505 | { name = "watchfiles" }, 506 | { name = "websockets" }, 507 | ] 508 | 509 | [[package]] 510 | name = "uvloop" 511 | version = "0.22.1" 512 | source = { registry = "https://pypi.org/simple" } 513 | sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } 514 | wheels = [ 515 | { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, 516 | { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, 517 | { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, 518 | { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, 519 | { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, 520 | { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, 521 | { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, 522 | { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, 523 | { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, 524 | { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, 525 | { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, 526 | { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, 527 | { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, 528 | { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, 529 | { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, 530 | { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, 531 | { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, 532 | { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, 533 | { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, 534 | { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, 535 | { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, 536 | { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, 537 | { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, 538 | { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, 539 | { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, 540 | { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, 541 | { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, 542 | { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, 543 | { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, 544 | { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, 545 | ] 546 | 547 | [[package]] 548 | name = "watchfiles" 549 | version = "1.1.1" 550 | source = { registry = "https://pypi.org/simple" } 551 | dependencies = [ 552 | { name = "anyio" }, 553 | ] 554 | sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } 555 | wheels = [ 556 | { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, 557 | { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, 558 | { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, 559 | { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, 560 | { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, 561 | { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, 562 | { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, 563 | { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, 564 | { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, 565 | { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, 566 | { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, 567 | { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, 568 | { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, 569 | { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, 570 | { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, 571 | { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, 572 | { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, 573 | { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, 574 | { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, 575 | { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, 576 | { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, 577 | { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, 578 | { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, 579 | { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, 580 | { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, 581 | { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, 582 | { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, 583 | { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, 584 | { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, 585 | { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, 586 | { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, 587 | { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, 588 | { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, 589 | { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, 590 | { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, 591 | { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, 592 | { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, 593 | { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, 594 | { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, 595 | { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, 596 | { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, 597 | { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, 598 | { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, 599 | { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, 600 | { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, 601 | { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, 602 | { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, 603 | { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, 604 | { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, 605 | { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, 606 | { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, 607 | { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, 608 | { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, 609 | { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, 610 | { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, 611 | { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, 612 | { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, 613 | { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, 614 | { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, 615 | { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, 616 | { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, 617 | { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, 618 | { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, 619 | { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, 620 | { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, 621 | { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, 622 | { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, 623 | { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, 624 | { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, 625 | { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, 626 | { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, 627 | { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, 628 | { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, 629 | { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, 630 | { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, 631 | { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, 632 | ] 633 | 634 | [[package]] 635 | name = "websockets" 636 | version = "15.0.1" 637 | source = { registry = "https://pypi.org/simple" } 638 | sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } 639 | wheels = [ 640 | { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, 641 | { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, 642 | { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, 643 | { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, 644 | { url = "https://files.pythonhosted.org/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", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, 645 | { url = "https://files.pythonhosted.org/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", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, 646 | { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, 647 | { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, 648 | { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, 649 | { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, 650 | { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, 651 | { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, 652 | { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, 653 | { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, 654 | { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, 655 | { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, 656 | { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, 657 | { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, 658 | { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, 659 | { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, 660 | { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, 661 | { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, 662 | { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, 663 | { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, 664 | { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, 665 | { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, 666 | { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, 667 | { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, 668 | { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, 669 | { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, 670 | { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, 671 | { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, 672 | { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, 673 | { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, 674 | ] 675 | --------------------------------------------------------------------------------