diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..aa96e1820a29689a9163b6a202ac15bf5d81fd8e --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +ENVIRONMENT=development +HF_TOKEN=xxx +HF_HOME=.cache diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..12fab87e136c26d3a940ee67e56db0f900cda522 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +# Build frontend +FROM node:18 as frontend-build +WORKDIR /app +COPY frontend/package*.json ./ +RUN npm install +COPY frontend/ ./ + +RUN npm run build + +# Build backend +FROM python:3.12-slim +WORKDIR /app + +# Create non-root user +RUN useradd -m -u 1000 user + +# Install poetry +RUN pip install poetry + +# Create and configure cache directory +RUN mkdir -p /app/.cache && \ + chown -R user:user /app + +# Copy and install backend dependencies +COPY backend/pyproject.toml backend/poetry.lock* ./ +RUN poetry config virtualenvs.create false \ + && poetry install --no-interaction --no-ansi --no-root --only main + +# Copy backend code +COPY backend/ . + +# Install Node.js and npm +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + netcat-openbsd \ + git \ + && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Copy frontend server and build +COPY --from=frontend-build /app/build ./frontend/build +COPY --from=frontend-build /app/package*.json ./frontend/ +COPY --from=frontend-build /app/server.js ./frontend/ + +# Install frontend production dependencies +WORKDIR /app/frontend +RUN npm install --production +WORKDIR /app + +# Environment variables +ENV HF_HOME=/app/.cache \ + TRANSFORMERS_CACHE=/app/.cache \ + HF_DATASETS_CACHE=/app/.cache \ + INTERNAL_API_PORT=7861 \ + PORT=7860 \ + NODE_ENV=production + + +# Note: HF_TOKEN should be provided at runtime, not build time +USER user +EXPOSE 7860 + +# Start both servers with wait-for +CMD ["sh", "-c", "env && uvicorn app.asgi:app --host 0.0.0.0 --port 7861 & while ! nc -z localhost 7861; do sleep 1; done && cd frontend && npm run serve"] diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev new file mode 100644 index 0000000000000000000000000000000000000000..f802c87f0d5d730c559b1f21ed715b48cc9ca42a --- /dev/null +++ b/backend/Dockerfile.dev @@ -0,0 +1,25 @@ +FROM python:3.12-slim + +WORKDIR /app + +# Install required system dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Install poetry +RUN pip install poetry + +# Copy Poetry configuration files +COPY pyproject.toml poetry.lock* ./ + +# Install dependencies +RUN poetry config virtualenvs.create false && \ + poetry install --no-interaction --no-ansi --no-root + +# Environment variables configuration for logs +ENV PYTHONUNBUFFERED=1 +ENV LOG_LEVEL=INFO + +# In dev, mount volume directly +CMD ["uvicorn", "app.asgi:app", "--host", "0.0.0.0", "--port", "7860", "--reload", "--log-level", "warning", "--no-access-log"] \ No newline at end of file diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000000000000000000000000000000000000..df5d19f331d4bdadc811c8cac0a7717259202f03 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,352 @@ +# Backend - Open LLM Leaderboard 🏆 + +FastAPI backend for the Open LLM Leaderboard. This service is part of a larger architecture that includes a React frontend. For complete project installation, see the [main README](../README.md). + +## ✨ Features + +- 📊 REST API for LLM models leaderboard management +- 🗳️ Voting and ranking system +- 🔄 HuggingFace Hub integration +- 🚀 Caching and performance optimizations + +## 🏗 Architecture + +```mermaid +flowchart TD + Client(["**Frontend**

React Application"]) --> API["**API Server**

FastAPI REST Endpoints"] + + subgraph Backend + API --> Core["**Core Layer**

• Middleware
• Cache
• Rate Limiting"] + Core --> Services["**Services Layer**

• Business Logic
• Data Processing"] + + subgraph Services Layer + Services --> Models["**Model Service**

• Model Submission
• Evaluation Pipeline"] + Services --> Votes["**Vote Service**

• Vote Management
• Data Synchronization"] + Services --> Board["**Leaderboard Service**

• Rankings
• Performance Metrics"] + end + + Models --> Cache["**Cache Layer**

• In-Memory Store
• Auto Invalidation"] + Votes --> Cache + Board --> Cache + + Models --> HF["**HuggingFace Hub**

• Models Repository
• Datasets Access"] + Votes --> HF + Board --> HF + end + + style Client fill:#f9f,stroke:#333,stroke-width:2px + style Models fill:#bbf,stroke:#333,stroke-width:2px + style Votes fill:#bbf,stroke:#333,stroke-width:2px + style Board fill:#bbf,stroke:#333,stroke-width:2px + style HF fill:#bfb,stroke:#333,stroke-width:2px +``` + +## 🛠️ HuggingFace Datasets + +The application uses several datasets on the HuggingFace Hub: + +### 1. Requests Dataset (`{HF_ORGANIZATION}/requests`) + +- **Operations**: + - 📤 `POST /api/models/submit`: Adds a JSON file for each new model submission + - 📥 `GET /api/models/status`: Reads files to get models status +- **Format**: One JSON file per model with submission details +- **Updates**: On each new model submission + +### 2. Votes Dataset (`{HF_ORGANIZATION}/votes`) + +- **Operations**: + - 📤 `POST /api/votes/{model_id}`: Adds a new vote + - 📥 `GET /api/votes/model/{provider}/{model}`: Reads model votes + - 📥 `GET /api/votes/user/{user_id}`: Reads user votes +- **Format**: JSONL with one vote per line +- **Sync**: Bidirectional between local cache and Hub + +### 3. Contents Dataset (`{HF_ORGANIZATION}/contents`) + +- **Operations**: + - 📥 `GET /api/leaderboard`: Reads raw data + - 📥 `GET /api/leaderboard/formatted`: Reads and formats data +- **Format**: Main dataset containing all scores and metrics +- **Updates**: Automatic after model evaluations + +### 4. Maintainers Highlight Dataset (`{HF_ORGANIZATION}/maintainers-highlight`) + +- **Operations**: + - 📥 Read-only access for highlighted models +- **Format**: List of models selected by maintainers +- **Updates**: Manual by maintainers + +## 🛠 Local Development + +### Prerequisites + +- Python 3.9+ +- [Poetry](https://python-poetry.org/docs/#installation) + +### Standalone Installation (without Docker) + +```bash +# Install dependencies +poetry install + +# Setup configuration +cp .env.example .env + +# Start development server +poetry run uvicorn app.asgi:app --host 0.0.0.0 --port 7860 --reload +``` + +Server will be available at http://localhost:7860 + +## ⚙️ Configuration + +| Variable | Description | Default | +| ------------ | ------------------------------------ | ----------- | +| ENVIRONMENT | Environment (development/production) | development | +| HF_TOKEN | HuggingFace authentication token | - | +| PORT | Server port | 7860 | +| LOG_LEVEL | Logging level (INFO/DEBUG/WARNING) | INFO | +| CORS_ORIGINS | Allowed CORS origins | ["*"] | +| CACHE_TTL | Cache Time To Live in seconds | 300 | + +## 🔧 Middleware + +The backend uses several middleware layers for optimal performance and security: + +- **CORS Middleware**: Handles Cross-Origin Resource Sharing +- **GZIP Middleware**: Compresses responses > 500 bytes +- **Rate Limiting**: Prevents API abuse +- **Caching**: In-memory caching with automatic invalidation + +## 📝 Logging + +The application uses a structured logging system with: + +- Formatted console output +- Different log levels per component +- Request/Response logging +- Performance metrics +- Error tracking + +## 📁 File Structure + +``` +backend/ +├── app/ # Source code +│ ├── api/ # Routes and endpoints +│ │ └── endpoints/ # Endpoint handlers +│ ├── core/ # Configurations +│ ├── services/ # Business logic +│ └── utils/ # Utilities +└── tests/ # Tests +``` + +## 📚 API + +Swagger documentation available at http://localhost:7860/docs + +### Main Endpoints & Data Structures + +#### Leaderboard + +- `GET /api/leaderboard/formatted` - Formatted data with computed fields and metadata + + ```typescript + Response { + models: [{ + id: string, // eval_name + model: { + name: string, // fullname + sha: string, // Model sha + precision: string, // e.g. "fp16", "int8" + type: string, // e.g. "fined-tuned-on-domain-specific-dataset" + weight_type: string, + architecture: string, + average_score: number, + has_chat_template: boolean + }, + evaluations: { + ifeval: { + name: "IFEval", + value: number, // Raw score + normalized_score: number + }, + bbh: { + name: "BBH", + value: number, + normalized_score: number + }, + math: { + name: "MATH Level 5", + value: number, + normalized_score: number + }, + gpqa: { + name: "GPQA", + value: number, + normalized_score: number + }, + musr: { + name: "MUSR", + value: number, + normalized_score: number + }, + mmlu_pro: { + name: "MMLU-PRO", + value: number, + normalized_score: number + } + }, + features: { + is_not_available_on_hub: boolean, + is_merged: boolean, + is_moe: boolean, + is_flagged: boolean, + is_highlighted_by_maintainer: boolean + }, + metadata: { + upload_date: string, + submission_date: string, + generation: string, + base_model: string, + hub_license: string, + hub_hearts: number, + params_billions: number, + co2_cost: number // CO₂ cost in kg + } + }] + } + ``` + +- `GET /api/leaderboard` - Raw data from the HuggingFace dataset + ```typescript + Response { + models: [{ + eval_name: string, + Precision: string, + Type: string, + "Weight type": string, + Architecture: string, + Model: string, + fullname: string, + "Model sha": string, + "Average ⬆️": number, + "Hub License": string, + "Hub ❤️": number, + "#Params (B)": number, + "Available on the hub": boolean, + Merged: boolean, + MoE: boolean, + Flagged: boolean, + "Chat Template": boolean, + "CO₂ cost (kg)": number, + "IFEval Raw": number, + IFEval: number, + "BBH Raw": number, + BBH: number, + "MATH Lvl 5 Raw": number, + "MATH Lvl 5": number, + "GPQA Raw": number, + GPQA: number, + "MUSR Raw": number, + MUSR: number, + "MMLU-PRO Raw": number, + "MMLU-PRO": number, + "Maintainer's Highlight": boolean, + "Upload To Hub Date": string, + "Submission Date": string, + Generation: string, + "Base Model": string + }] + } + ``` + +#### Models + +- `GET /api/models/status` - Get all models grouped by status + ```typescript + Response { + pending: [{ + name: string, + submitter: string, + revision: string, + wait_time: string, + submission_time: string, + status: "PENDING" | "EVALUATING" | "FINISHED", + precision: string + }], + evaluating: Array, + finished: Array + } + ``` +- `GET /api/models/pending` - Get pending models only +- `POST /api/models/submit` - Submit model + + ```typescript + Request { + user_id: string, + model_id: string, + base_model?: string, + precision?: string, + model_type: string + } + + Response { + status: string, + message: string + } + ``` + +- `GET /api/models/{model_id}/status` - Get model status + +#### Votes + +- `POST /api/votes/{model_id}` - Vote + + ```typescript + Request { + vote_type: "up" | "down", + user_id: string // HuggingFace username + } + + Response { + success: boolean, + message: string + } + ``` + +- `GET /api/votes/model/{provider}/{model}` - Get model votes + ```typescript + Response { + total_votes: number, + up_votes: number, + down_votes: number + } + ``` +- `GET /api/votes/user/{user_id}` - Get user votes + ```typescript + Response Array<{ + model_id: string, + vote_type: string, + timestamp: string + }> + ``` + +## 🔒 Authentication + +The backend uses HuggingFace token-based authentication for secure API access. Make sure to: + +1. Set your HF_TOKEN in the .env file +2. Include the token in API requests via Bearer authentication +3. Keep your token secure and never commit it to version control + +## 🚀 Performance + +The backend implements several optimizations: + +- In-memory caching with configurable TTL (Time To Live) +- Batch processing for model evaluations +- Rate limiting for API endpoints +- Efficient database queries with proper indexing +- Automatic cache invalidation for votes diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/app/api/__init__.py b/backend/app/api/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..41bd81293794127ec484666c9a9bf3b2cd0bbe3c --- /dev/null +++ b/backend/app/api/__init__.py @@ -0,0 +1,5 @@ +""" +API package initialization +""" + +__all__ = ["endpoints"] diff --git a/backend/app/api/dependencies.py b/backend/app/api/dependencies.py new file mode 100644 index 0000000000000000000000000000000000000000..89b98e0cdea59cf0b395722bdb473c71aee48496 --- /dev/null +++ b/backend/app/api/dependencies.py @@ -0,0 +1,34 @@ +from fastapi import Depends, HTTPException +import logging +from app.services.models import ModelService +from app.services.votes import VoteService +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) + +model_service = ModelService() +vote_service = VoteService() + +async def get_model_service() -> ModelService: + """Dependency to get ModelService instance""" + try: + logger.info(LogFormatter.info("Initializing model service dependency")) + await model_service.initialize() + logger.info(LogFormatter.success("Model service initialized")) + return model_service + except Exception as e: + error_msg = "Failed to initialize model service" + logger.error(LogFormatter.error(error_msg, e)) + raise HTTPException(status_code=500, detail=str(e)) + +async def get_vote_service() -> VoteService: + """Dependency to get VoteService instance""" + try: + logger.info(LogFormatter.info("Initializing vote service dependency")) + await vote_service.initialize() + logger.info(LogFormatter.success("Vote service initialized")) + return vote_service + except Exception as e: + error_msg = "Failed to initialize vote service" + logger.error(LogFormatter.error(error_msg, e)) + raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/backend/app/api/endpoints/leaderboard.py b/backend/app/api/endpoints/leaderboard.py new file mode 100644 index 0000000000000000000000000000000000000000..cf15be68b9d5e36b5a1b3f97b533ff3a7766764a --- /dev/null +++ b/backend/app/api/endpoints/leaderboard.py @@ -0,0 +1,49 @@ +from fastapi import APIRouter +from typing import List, Dict, Any +from app.services.leaderboard import LeaderboardService +from app.core.fastapi_cache import cached, build_cache_key +import logging +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) +router = APIRouter() +leaderboard_service = LeaderboardService() + +def leaderboard_key_builder(func, namespace: str = "leaderboard", **kwargs): + """Build cache key for leaderboard data""" + key_type = "raw" if func.__name__ == "get_leaderboard" else "formatted" + key = build_cache_key(namespace, key_type) + logger.debug(LogFormatter.info(f"Built leaderboard cache key: {key}")) + return key + +@router.get("") +@cached(expire=300, key_builder=leaderboard_key_builder) +async def get_leaderboard() -> List[Dict[str, Any]]: + """ + Get raw leaderboard data + Response will be automatically GZIP compressed if size > 500 bytes + """ + try: + logger.info(LogFormatter.info("Fetching raw leaderboard data")) + data = await leaderboard_service.fetch_raw_data() + logger.info(LogFormatter.success(f"Retrieved {len(data)} leaderboard entries")) + return data + except Exception as e: + logger.error(LogFormatter.error("Failed to fetch raw leaderboard data", e)) + raise + +@router.get("/formatted") +@cached(expire=300, key_builder=leaderboard_key_builder) +async def get_formatted_leaderboard() -> List[Dict[str, Any]]: + """ + Get formatted leaderboard data with restructured objects + Response will be automatically GZIP compressed if size > 500 bytes + """ + try: + logger.info(LogFormatter.info("Fetching formatted leaderboard data")) + data = await leaderboard_service.get_formatted_data() + logger.info(LogFormatter.success(f"Retrieved {len(data)} formatted entries")) + return data + except Exception as e: + logger.error(LogFormatter.error("Failed to fetch formatted leaderboard data", e)) + raise \ No newline at end of file diff --git a/backend/app/api/endpoints/models.py b/backend/app/api/endpoints/models.py new file mode 100644 index 0000000000000000000000000000000000000000..e8361c35ab2abfd3a75f6232bff725404b43b377 --- /dev/null +++ b/backend/app/api/endpoints/models.py @@ -0,0 +1,103 @@ +from fastapi import APIRouter, HTTPException, Depends +from typing import Dict, Any, List +import logging +from app.services.models import ModelService +from app.api.dependencies import get_model_service +from app.core.fastapi_cache import cached +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) +router = APIRouter(tags=["models"]) + +@router.get("/status") +@cached(expire=300) +async def get_models_status( + model_service: ModelService = Depends(get_model_service) +) -> Dict[str, List[Dict[str, Any]]]: + """Get all models grouped by status""" + try: + logger.info(LogFormatter.info("Fetching status for all models")) + result = await model_service.get_models() + stats = { + status: len(models) for status, models in result.items() + } + for line in LogFormatter.stats(stats, "Models by Status"): + logger.info(line) + return result + except Exception as e: + logger.error(LogFormatter.error("Failed to get models status", e)) + raise HTTPException(status_code=500, detail=str(e)) + +@router.get("/pending") +@cached(expire=60) +async def get_pending_models( + model_service: ModelService = Depends(get_model_service) +) -> List[Dict[str, Any]]: + """Get all models waiting for evaluation""" + try: + logger.info(LogFormatter.info("Fetching pending models")) + models = await model_service.get_models() + pending = models.get("pending", []) + logger.info(LogFormatter.success(f"Found {len(pending)} pending models")) + return pending + except Exception as e: + logger.error(LogFormatter.error("Failed to get pending models", e)) + raise HTTPException(status_code=500, detail=str(e)) + +@router.post("/submit") +async def submit_model( + model_data: Dict[str, Any], + model_service: ModelService = Depends(get_model_service) +) -> Dict[str, Any]: + try: + logger.info(LogFormatter.section("MODEL SUBMISSION")) + + user_id = model_data.pop('user_id', None) + if not user_id: + error_msg = "user_id is required" + logger.error(LogFormatter.error("Validation failed", error_msg)) + raise ValueError(error_msg) + + # Log submission details + submission_info = { + "Model_ID": model_data.get("model_id"), + "User": user_id, + "Base_Model": model_data.get("base_model"), + "Precision": model_data.get("precision"), + "Model_Type": model_data.get("model_type") + } + for line in LogFormatter.tree(submission_info, "Submission Details"): + logger.info(line) + + result = await model_service.submit_model(model_data, user_id) + logger.info(LogFormatter.success("Model submitted successfully")) + return result + + except ValueError as e: + logger.error(LogFormatter.error("Invalid submission data", e)) + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(LogFormatter.error("Submission failed", e)) + raise HTTPException(status_code=500, detail=str(e)) + +@router.get("/{model_id}/status") +async def get_model_status( + model_id: str, + model_service: ModelService = Depends(get_model_service) +) -> Dict[str, Any]: + try: + logger.info(LogFormatter.info(f"Checking status for model: {model_id}")) + status = await model_service.get_model_status(model_id) + + if status["status"] != "not_found": + logger.info(LogFormatter.success("Status found")) + for line in LogFormatter.tree(status, "Model Status"): + logger.info(line) + else: + logger.warning(LogFormatter.warning(f"No status found for model: {model_id}")) + + return status + + except Exception as e: + logger.error(LogFormatter.error("Failed to get model status", e)) + raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/backend/app/api/endpoints/votes.py b/backend/app/api/endpoints/votes.py new file mode 100644 index 0000000000000000000000000000000000000000..6d2a3b64add9b8ef0950366b38eca469b3788bf6 --- /dev/null +++ b/backend/app/api/endpoints/votes.py @@ -0,0 +1,105 @@ +from fastapi import APIRouter, HTTPException, Query, Depends +from typing import Dict, Any, List +from app.services.votes import VoteService +from app.core.fastapi_cache import cached, build_cache_key, invalidate_cache_key +import logging +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) +router = APIRouter() +vote_service = VoteService() + +def model_votes_key_builder(func, namespace: str = "model_votes", **kwargs): + """Build cache key for model votes""" + provider = kwargs.get('provider') + model = kwargs.get('model') + key = build_cache_key(namespace, provider, model) + logger.debug(LogFormatter.info(f"Built model votes cache key: {key}")) + return key + +def user_votes_key_builder(func, namespace: str = "user_votes", **kwargs): + """Build cache key for user votes""" + user_id = kwargs.get('user_id') + key = build_cache_key(namespace, user_id) + logger.debug(LogFormatter.info(f"Built user votes cache key: {key}")) + return key + +@router.post("/{model_id:path}") +async def add_vote( + model_id: str, + vote_type: str = Query(..., description="Type of vote (up/down)"), + user_id: str = Query(..., description="HuggingFace username") +) -> Dict[str, Any]: + try: + logger.info(LogFormatter.section("ADDING VOTE")) + stats = { + "Model": model_id, + "User": user_id, + "Type": vote_type + } + for line in LogFormatter.tree(stats, "Vote Details"): + logger.info(line) + + await vote_service.initialize() + result = await vote_service.add_vote(model_id, user_id, vote_type) + + # Invalidate affected caches + try: + logger.info(LogFormatter.subsection("CACHE INVALIDATION")) + provider, model = model_id.split('/', 1) + + # Build and invalidate cache keys + model_cache_key = build_cache_key("model_votes", provider, model) + user_cache_key = build_cache_key("user_votes", user_id) + + invalidate_cache_key(model_cache_key) + invalidate_cache_key(user_cache_key) + + cache_stats = { + "Model_Cache": model_cache_key, + "User_Cache": user_cache_key + } + for line in LogFormatter.tree(cache_stats, "Invalidated Caches"): + logger.info(line) + + except Exception as e: + logger.error(LogFormatter.error("Failed to invalidate cache", e)) + + return result + except Exception as e: + logger.error(LogFormatter.error("Failed to add vote", e)) + raise HTTPException(status_code=400, detail=str(e)) + +@router.get("/model/{provider}/{model}") +@cached(expire=60, key_builder=model_votes_key_builder) +async def get_model_votes( + provider: str, + model: str +) -> Dict[str, Any]: + """Get all votes for a specific model""" + try: + logger.info(LogFormatter.info(f"Fetching votes for model: {provider}/{model}")) + await vote_service.initialize() + model_id = f"{provider}/{model}" + result = await vote_service.get_model_votes(model_id) + logger.info(LogFormatter.success(f"Found {result.get('total_votes', 0)} votes")) + return result + except Exception as e: + logger.error(LogFormatter.error("Failed to get model votes", e)) + raise HTTPException(status_code=400, detail=str(e)) + +@router.get("/user/{user_id}") +@cached(expire=60, key_builder=user_votes_key_builder) +async def get_user_votes( + user_id: str +) -> List[Dict[str, Any]]: + """Get all votes from a specific user""" + try: + logger.info(LogFormatter.info(f"Fetching votes for user: {user_id}")) + await vote_service.initialize() + votes = await vote_service.get_user_votes(user_id) + logger.info(LogFormatter.success(f"Found {len(votes)} votes")) + return votes + except Exception as e: + logger.error(LogFormatter.error("Failed to get user votes", e)) + raise HTTPException(status_code=400, detail=str(e)) \ No newline at end of file diff --git a/backend/app/api/router.py b/backend/app/api/router.py new file mode 100644 index 0000000000000000000000000000000000000000..a2c952105c729b92abc72d59ae5882ee4394c017 --- /dev/null +++ b/backend/app/api/router.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter + +from app.api.endpoints import leaderboard, votes, models + +router = APIRouter() + +router.include_router(leaderboard.router, prefix="/leaderboard", tags=["leaderboard"]) +router.include_router(votes.router, prefix="/votes", tags=["votes"]) +router.include_router(models.router, prefix="/models", tags=["models"]) \ No newline at end of file diff --git a/backend/app/asgi.py b/backend/app/asgi.py new file mode 100644 index 0000000000000000000000000000000000000000..bd5bd4d904e0d7e3e999dc51c5e924873d78c2a1 --- /dev/null +++ b/backend/app/asgi.py @@ -0,0 +1,106 @@ +""" +ASGI entry point for the Open Greek Financial LLM Leaderboard API. +""" +import os +import uvicorn +import logging +import logging.config +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.middleware.gzip import GZipMiddleware +import sys + +from app.api.router import router +from app.core.fastapi_cache import setup_cache +from app.utils.logging import LogFormatter +from app.config import hf_config + +# Configure logging before anything else +LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": True, + "formatters": { + "default": { + "format": "%(name)s - %(levelname)s - %(message)s", + } + }, + "handlers": { + "default": { + "formatter": "default", + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", + } + }, + "loggers": { + "uvicorn": { + "handlers": ["default"], + "level": "WARNING", + "propagate": False, + }, + "uvicorn.error": { + "level": "WARNING", + "handlers": ["default"], + "propagate": False, + }, + "uvicorn.access": { + "handlers": ["default"], + "level": "WARNING", + "propagate": False, + }, + "app": { + "handlers": ["default"], + "level": "WARNING", + "propagate": False, + } + }, + "root": { + "handlers": ["default"], + "level": "WARNING", + } +} + +# Apply logging configuration +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger("app") + +# Create FastAPI application +app = FastAPI( + title="Open Greek Financial LLM Leaderboard", + version="1.0.0", + docs_url="/docs", +) + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Add GZIP compression +app.add_middleware(GZipMiddleware, minimum_size=500) + +# Include API router +app.include_router(router, prefix="/api") + +@app.on_event("startup") +async def startup_event(): + """Initialize services on startup""" + logger.info("\n") + logger.info(LogFormatter.section("APPLICATION STARTUP")) + + # Log HF configuration + logger.info(LogFormatter.section("HUGGING FACE CONFIGURATION")) + logger.info(LogFormatter.info(f"Organization: {hf_config.HF_ORGANIZATION}")) + logger.info(LogFormatter.info(f"Token Status: {'Present' if hf_config.HF_TOKEN else 'Missing'}")) + logger.info(LogFormatter.info(f"Using repositories:")) + logger.info(LogFormatter.info(f" - Queue: {hf_config.QUEUE_REPO}")) + logger.info(LogFormatter.info(f" - Aggregated: {hf_config.AGGREGATED_REPO}")) + logger.info(LogFormatter.info(f" - Votes: {hf_config.VOTES_REPO}")) + logger.info(LogFormatter.info(f" - Maintainers Highlight: {hf_config.MAINTAINERS_HIGHLIGHT_REPO}")) + + # Setup cache + setup_cache() + logger.info(LogFormatter.success("FastAPI Cache initialized with in-memory backend")) diff --git a/backend/app/config/__init__.py b/backend/app/config/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9a8cea98b9ddb1daaf3c9e8e5d2c9be1fc94657e --- /dev/null +++ b/backend/app/config/__init__.py @@ -0,0 +1,6 @@ +""" +Configuration module for the Open LLM Leaderboard backend. +All configuration values are imported from base.py to avoid circular dependencies. +""" + +from .base import * diff --git a/backend/app/config/base.py b/backend/app/config/base.py new file mode 100644 index 0000000000000000000000000000000000000000..8efce683f5a1c100e164634e0883db42ff196728 --- /dev/null +++ b/backend/app/config/base.py @@ -0,0 +1,38 @@ +import os +from pathlib import Path + +# Server configuration +HOST = "0.0.0.0" +PORT = 7860 +WORKERS = 4 +RELOAD = True if os.environ.get("ENVIRONMENT") == "development" else False + +# CORS configuration +ORIGINS = ["http://localhost:3000"] if os.getenv("ENVIRONMENT") == "development" else ["*"] + +# Cache configuration +CACHE_TTL = int(os.environ.get("CACHE_TTL", 300)) # 5 minutes default + +# Rate limiting +RATE_LIMIT_PERIOD = 7 # days +RATE_LIMIT_QUOTA = 5 +HAS_HIGHER_RATE_LIMIT = [] + +# HuggingFace configuration +HF_TOKEN = os.environ.get("HF_TOKEN") +HF_ORGANIZATION = "TheFinAI" +API = { + "INFERENCE": "https://api-inference.huggingface.co/models", + "HUB": "https://huggingface.co" +} + +# Cache paths +CACHE_ROOT = Path(os.environ.get("HF_HOME", ".cache")) +DATASETS_CACHE = CACHE_ROOT / "datasets" +MODELS_CACHE = CACHE_ROOT / "models" +VOTES_CACHE = CACHE_ROOT / "votes" +EVAL_CACHE = CACHE_ROOT / "eval-queue" + +# Repository configuration +QUEUE_REPO = f"{HF_ORGANIZATION}/requests" +EVAL_REQUESTS_PATH = EVAL_CACHE / "eval_requests.jsonl" \ No newline at end of file diff --git a/backend/app/config/hf_config.py b/backend/app/config/hf_config.py new file mode 100644 index 0000000000000000000000000000000000000000..82b520c8edea0ec1b1994cf34c37ca95d6cbb3ed --- /dev/null +++ b/backend/app/config/hf_config.py @@ -0,0 +1,34 @@ +""" +Hugging Face configuration module +""" +import os +import logging +from typing import Optional +from huggingface_hub import HfApi +from pathlib import Path +from app.core.cache import cache_config +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) + +# Organization or user who owns the datasets +HF_ORGANIZATION = "TheFinAI" + +# Get HF token directly from environment +HF_TOKEN = os.environ.get("HF_TOKEN") +if not HF_TOKEN: + logger.warning("HF_TOKEN not found in environment variables. Some features may be limited.") + +# Initialize HF API +API = HfApi(token=HF_TOKEN) + +# Repository configuration +QUEUE_REPO = f"{HF_ORGANIZATION}/requests" +AGGREGATED_REPO = f"{HF_ORGANIZATION}/greek-contents" +VOTES_REPO = f"{HF_ORGANIZATION}/votes" +MAINTAINERS_HIGHLIGHT_REPO = f"{HF_ORGANIZATION}/maintainers-highlight" + +# File paths from cache config +VOTES_PATH = cache_config.votes_file +EVAL_REQUESTS_PATH = cache_config.eval_requests_file +MODEL_CACHE_DIR = cache_config.models_cache diff --git a/backend/app/config/logging_config.py b/backend/app/config/logging_config.py new file mode 100644 index 0000000000000000000000000000000000000000..96be6f6749cdd79defb975141d857ff216aac420 --- /dev/null +++ b/backend/app/config/logging_config.py @@ -0,0 +1,38 @@ +import logging +import sys +from tqdm import tqdm + +def get_tqdm_handler(): + """ + Creates a special handler for tqdm that doesn't interfere with other logs. + """ + class TqdmLoggingHandler(logging.Handler): + def emit(self, record): + try: + msg = self.format(record) + tqdm.write(msg) + self.flush() + except Exception: + self.handleError(record) + + return TqdmLoggingHandler() + +def setup_service_logger(service_name: str) -> logging.Logger: + """ + Configure a specific logger for a given service. + """ + logger = logging.getLogger(f"app.services.{service_name}") + + # If the logger already has handlers, don't reconfigure it + if logger.handlers: + return logger + + # Add tqdm handler for this service + tqdm_handler = get_tqdm_handler() + tqdm_handler.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s')) + logger.addHandler(tqdm_handler) + + # Don't propagate logs to parent loggers + logger.propagate = False + + return logger \ No newline at end of file diff --git a/backend/app/core/cache.py b/backend/app/core/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..28da6c2f09ef08b0df3393c6c97f9fc412ae13eb --- /dev/null +++ b/backend/app/core/cache.py @@ -0,0 +1,109 @@ +import os +import shutil +from pathlib import Path +from datetime import timedelta +import logging +from app.utils.logging import LogFormatter +from app.config.base import ( + CACHE_ROOT, + DATASETS_CACHE, + MODELS_CACHE, + VOTES_CACHE, + EVAL_CACHE, + CACHE_TTL +) + +logger = logging.getLogger(__name__) + +class CacheConfig: + def __init__(self): + # Get cache paths from config + self.cache_root = CACHE_ROOT + self.datasets_cache = DATASETS_CACHE + self.models_cache = MODELS_CACHE + self.votes_cache = VOTES_CACHE + self.eval_cache = EVAL_CACHE + + # Specific files + self.votes_file = self.votes_cache / "votes_data.jsonl" + self.eval_requests_file = self.eval_cache / "eval_requests.jsonl" + + # Cache TTL + self.cache_ttl = timedelta(seconds=CACHE_TTL) + + self._initialize_cache_dirs() + self._setup_environment() + + def _initialize_cache_dirs(self): + """Initialize all necessary cache directories""" + try: + logger.info(LogFormatter.section("CACHE INITIALIZATION")) + + cache_dirs = { + "Root": self.cache_root, + "Datasets": self.datasets_cache, + "Models": self.models_cache, + "Votes": self.votes_cache, + "Eval": self.eval_cache + } + + for name, cache_dir in cache_dirs.items(): + cache_dir.mkdir(parents=True, exist_ok=True) + logger.info(LogFormatter.success(f"{name} cache directory: {cache_dir}")) + + except Exception as e: + logger.error(LogFormatter.error("Failed to create cache directories", e)) + raise + + def _setup_environment(self): + """Configure HuggingFace environment variables""" + logger.info(LogFormatter.subsection("ENVIRONMENT SETUP")) + + env_vars = { + "HF_HOME": str(self.cache_root), + "TRANSFORMERS_CACHE": str(self.models_cache), + "HF_DATASETS_CACHE": str(self.datasets_cache) + } + + for var, value in env_vars.items(): + os.environ[var] = value + logger.info(LogFormatter.info(f"Set {var}={value}")) + + def get_cache_path(self, cache_type: str) -> Path: + """Returns the path for a specific cache type""" + cache_paths = { + "datasets": self.datasets_cache, + "models": self.models_cache, + "votes": self.votes_cache, + "eval": self.eval_cache + } + return cache_paths.get(cache_type, self.cache_root) + + def flush_cache(self, cache_type: str = None): + """Flush specified cache or all caches if no type is specified""" + try: + if cache_type: + logger.info(LogFormatter.section(f"FLUSHING {cache_type.upper()} CACHE")) + cache_dir = self.get_cache_path(cache_type) + if cache_dir.exists(): + stats = { + "Cache_Type": cache_type, + "Directory": str(cache_dir) + } + for line in LogFormatter.tree(stats, "Cache Details"): + logger.info(line) + shutil.rmtree(cache_dir) + cache_dir.mkdir(parents=True, exist_ok=True) + logger.info(LogFormatter.success("Cache cleared successfully")) + else: + logger.info(LogFormatter.section("FLUSHING ALL CACHES")) + for cache_type in ["datasets", "models", "votes", "eval"]: + self.flush_cache(cache_type) + logger.info(LogFormatter.success("All caches cleared successfully")) + + except Exception as e: + logger.error(LogFormatter.error("Failed to flush cache", e)) + raise + +# Singleton instance of cache configuration +cache_config = CacheConfig() \ No newline at end of file diff --git a/backend/app/core/fastapi_cache.py b/backend/app/core/fastapi_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..51bd95ceccde3e3b613980266938ac190a296b30 --- /dev/null +++ b/backend/app/core/fastapi_cache.py @@ -0,0 +1,48 @@ +from fastapi_cache import FastAPICache +from fastapi_cache.backends.inmemory import InMemoryBackend +from fastapi_cache.decorator import cache +from datetime import timedelta +from app.config import CACHE_TTL +import logging +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) + +def setup_cache(): + """Initialize FastAPI Cache with in-memory backend""" + FastAPICache.init( + backend=InMemoryBackend(), + prefix="fastapi-cache", + expire=CACHE_TTL + ) + logger.info(LogFormatter.success("FastAPI Cache initialized with in-memory backend")) + +def invalidate_cache_key(key: str): + """Invalidate a specific cache key""" + try: + backend = FastAPICache.get_backend() + if hasattr(backend, 'delete'): + backend.delete(key) + logger.info(LogFormatter.success(f"Cache invalidated for key: {key}")) + else: + logger.warning(LogFormatter.warning("Cache backend does not support deletion")) + except Exception as e: + logger.error(LogFormatter.error(f"Failed to invalidate cache key: {key}", e)) + +def build_cache_key(namespace: str, *args) -> str: + """Build a consistent cache key""" + key = f"fastapi-cache:{namespace}:{':'.join(str(arg) for arg in args)}" + logger.debug(LogFormatter.info(f"Built cache key: {key}")) + return key + +def cached(expire: int = CACHE_TTL, key_builder=None): + """Decorator for caching endpoint responses + + Args: + expire (int): Cache TTL in seconds + key_builder (callable, optional): Custom key builder function + """ + return cache( + expire=expire, + key_builder=key_builder + ) \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000000000000000000000000000000000000..1ae560457970195226b193f2c616452187581de1 --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,18 @@ +from fastapi import FastAPI +from app.config.logging_config import setup_logging +import logging + +# Initialize logging configuration +setup_logging() +logger = logging.getLogger(__name__) + +app = FastAPI(title="Open Greek Financial LLM Leaderboard API") + +@app.on_event("startup") +async def startup_event(): + logger.info("Starting up the application...") + +# Import and include routers after app initialization +from app.api import models, votes +app.include_router(models.router, prefix="/api", tags=["models"]) +app.include_router(votes.router, prefix="/api", tags=["votes"]) diff --git a/backend/app/services/__init__.py b/backend/app/services/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..399192f82143e7bf446fa183fa9e7779adab2bd7 --- /dev/null +++ b/backend/app/services/__init__.py @@ -0,0 +1,3 @@ +from . import hf_service, leaderboard, votes, models + +__all__ = ["hf_service", "leaderboard", "votes", "models"] diff --git a/backend/app/services/hf_service.py b/backend/app/services/hf_service.py new file mode 100644 index 0000000000000000000000000000000000000000..c8e2a2bc09562cbae0cab56f5f7f8c74445adf53 --- /dev/null +++ b/backend/app/services/hf_service.py @@ -0,0 +1,50 @@ +from typing import Optional +from huggingface_hub import HfApi +from app.config import HF_TOKEN, API +from app.core.cache import cache_config +from app.utils.logging import LogFormatter +import logging + +logger = logging.getLogger(__name__) + +class HuggingFaceService: + def __init__(self): + self.api = API + self.token = HF_TOKEN + self.cache_dir = cache_config.models_cache + + async def check_authentication(self) -> bool: + """Check if the HF token is valid""" + if not self.token: + return False + try: + logger.info(LogFormatter.info("Checking HF token validity...")) + self.api.get_token_permission() + logger.info(LogFormatter.success("HF token is valid")) + return True + except Exception as e: + logger.error(LogFormatter.error("HF token validation failed", e)) + return False + + async def get_user_info(self) -> Optional[dict]: + """Get information about the authenticated user""" + try: + logger.info(LogFormatter.info("Fetching user information...")) + info = self.api.get_token_permission() + logger.info(LogFormatter.success(f"User info retrieved for: {info.get('user', 'Unknown')}")) + return info + except Exception as e: + logger.error(LogFormatter.error("Failed to get user info", e)) + return None + + def _log_repo_operation(self, operation: str, repo: str, details: str = None): + """Helper to log repository operations""" + logger.info(LogFormatter.section(f"HF REPOSITORY OPERATION - {operation.upper()}")) + stats = { + "Operation": operation, + "Repository": repo, + } + if details: + stats["Details"] = details + for line in LogFormatter.tree(stats): + logger.info(line) \ No newline at end of file diff --git a/backend/app/services/leaderboard.py b/backend/app/services/leaderboard.py new file mode 100644 index 0000000000000000000000000000000000000000..17b2b594feea3a08c745ea0c1103cba30dda4799 --- /dev/null +++ b/backend/app/services/leaderboard.py @@ -0,0 +1,203 @@ +from app.core.cache import cache_config +from datetime import datetime +from typing import List, Dict, Any +import datasets +from fastapi import HTTPException +import logging +from app.config.base import HF_ORGANIZATION +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) + +class LeaderboardService: + def __init__(self): + pass + + async def fetch_raw_data(self) -> List[Dict[str, Any]]: + """Fetch raw leaderboard data from HuggingFace dataset""" + try: + logger.info(LogFormatter.section("FETCHING LEADERBOARD DATA")) + logger.info(LogFormatter.info(f"Loading dataset from {HF_ORGANIZATION}/greek-contents")) + + dataset = datasets.load_dataset( + f"{HF_ORGANIZATION}/greek-contents", + cache_dir=cache_config.get_cache_path("datasets") + )["train"] + + df = dataset.to_pandas() + data = df.to_dict('records') + + stats = { + "Total_Entries": len(data), + "Dataset_Size": f"{df.memory_usage(deep=True).sum() / 1024 / 1024:.1f}MB" + } + for line in LogFormatter.stats(stats, "Dataset Statistics"): + logger.info(line) + + return data + + except Exception as e: + logger.error(LogFormatter.error("Failed to fetch leaderboard data", e)) + raise HTTPException(status_code=500, detail=str(e)) + + async def get_formatted_data(self) -> List[Dict[str, Any]]: + """Get formatted leaderboard data""" + try: + logger.info(LogFormatter.section("FORMATTING LEADERBOARD DATA")) + + raw_data = await self.fetch_raw_data() + formatted_data = [] + type_counts = {} + error_count = 0 + + # Initialize progress tracking + total_items = len(raw_data) + logger.info(LogFormatter.info(f"Processing {total_items:,} entries...")) + + for i, item in enumerate(raw_data, 1): + try: + formatted_item = await self.transform_data(item) + formatted_data.append(formatted_item) + + # Count model types + model_type = formatted_item["model"]["type"] + type_counts[model_type] = type_counts.get(model_type, 0) + 1 + + except Exception as e: + error_count += 1 + logger.error(LogFormatter.error(f"Failed to format entry {i}/{total_items}", e)) + continue + + # Log progress every 10% + if i % max(1, total_items // 10) == 0: + progress = (i / total_items) * 100 + logger.info(LogFormatter.info(f"Progress: {LogFormatter.progress_bar(i, total_items)}")) + + # Log final statistics + stats = { + "Total_Processed": total_items, + "Successful": len(formatted_data), + "Failed": error_count + } + logger.info(LogFormatter.section("PROCESSING SUMMARY")) + for line in LogFormatter.stats(stats, "Processing Statistics"): + logger.info(line) + + # Log model type distribution + type_stats = {f"Type_{k}": v for k, v in type_counts.items()} + logger.info(LogFormatter.subsection("MODEL TYPE DISTRIBUTION")) + for line in LogFormatter.stats(type_stats): + logger.info(line) + + return formatted_data + + except Exception as e: + logger.error(LogFormatter.error("Failed to format leaderboard data", e)) + raise HTTPException(status_code=500, detail=str(e)) + + async def transform_data(self, data: Dict[str, Any]) -> Dict[str, Any]: + """Transform raw data into the format expected by the frontend""" + try: + # Extract model name for logging + model_name = data.get("fullname", "Unknown") + logger.debug(LogFormatter.info(f"Transforming data for model: {model_name}")) + + # Create unique ID combining model name, precision, sha and chat template status + unique_id = f"{data.get('fullname', 'Unknown')}_{data.get('Precision', 'Unknown')}_{data.get('Model sha', 'Unknown')}_{str(data.get('Chat Template', False))}" + + evaluations = { + "multifin": { + "name": "MultiFin", + "value": data.get("MultiFin Raw", 0), + "normalized_score": data.get("MultiFin", 0) + }, + "qa": { + "name": "QA", + "value": data.get("QA Raw", 0), + "normalized_score": data.get("QA", 0) + }, + "fns": { + "name": "FNS", + "value": data.get("FNS Raw", 0), + "normalized_score": data.get("FNS", 0) + }, + "finnum": { + "name": "FinNum", + "value": data.get("FinNum Raw", 0), + "normalized_score": data.get("FinNum", 0) + }, + "fintext": { + "name": "FinText", + "value": data.get("FinText Raw", 0), + "normalized_score": data.get("FinText", 0) + }, + } + + features = { + "is_not_available_on_hub": data.get("Available on the hub", False), + "is_merged": data.get("Merged", False), + "is_moe": data.get("MoE", False), + "is_flagged": data.get("Flagged", False), + "is_highlighted_by_maintainer": data.get("Official Providers", False) + } + + metadata = { + "upload_date": data.get("Upload To Hub Date"), + "submission_date": data.get("Submission Date"), + "generation": data.get("Generation"), + "base_model": data.get("Base Model"), + "hub_license": data.get("Hub License"), + "hub_hearts": data.get("Hub ❤️"), + "params_billions": data.get("#Params (B)"), + "co2_cost": data.get("CO₂ cost (kg)", 0) + } + + # Clean model type by removing emojis if present + original_type = data.get("Type", "") + model_type = original_type.lower().strip() + + # Remove emojis and parentheses + if "(" in model_type: + model_type = model_type.split("(")[0].strip() + model_type = ''.join(c for c in model_type if not c in '🔶🟢🟩💬🤝🌸 ') + + # Map old model types to new ones + model_type_mapping = { + "fine-tuned": "fined-tuned-on-domain-specific-dataset", + "fine tuned": "fined-tuned-on-domain-specific-dataset", + "finetuned": "fined-tuned-on-domain-specific-dataset", + "fine_tuned": "fined-tuned-on-domain-specific-dataset", + "ft": "fined-tuned-on-domain-specific-dataset", + "finetuning": "fined-tuned-on-domain-specific-dataset", + "fine tuning": "fined-tuned-on-domain-specific-dataset", + "fine-tuning": "fined-tuned-on-domain-specific-dataset" + } + + mapped_type = model_type_mapping.get(model_type.lower().strip(), model_type) + + if mapped_type != model_type: + logger.debug(LogFormatter.info(f"Model type mapped: {original_type} -> {mapped_type}")) + + transformed_data = { + "id": unique_id, + "model": { + "name": data.get("fullname"), + "sha": data.get("Model sha"), + "precision": data.get("Precision"), + "type": mapped_type, + "weight_type": data.get("Weight type"), + "architecture": data.get("Architecture"), + "average_score": data.get("Average ⬆️"), + "has_chat_template": data.get("Chat Template", False) + }, + "evaluations": evaluations, + "features": features, + "metadata": metadata + } + + logger.debug(LogFormatter.success(f"Successfully transformed data for {model_name}")) + return transformed_data + + except Exception as e: + logger.error(LogFormatter.error(f"Failed to transform data for {data.get('fullname', 'Unknown')}", e)) + raise diff --git a/backend/app/services/models.py b/backend/app/services/models.py new file mode 100644 index 0000000000000000000000000000000000000000..111cd1dc936cd169a60c11d1886989e4cb89beaa --- /dev/null +++ b/backend/app/services/models.py @@ -0,0 +1,579 @@ +from datetime import datetime, timezone +from typing import Dict, Any, Optional, List +import json +import os +from pathlib import Path +import logging +import aiohttp +import asyncio +import time +from huggingface_hub import HfApi, CommitOperationAdd +from huggingface_hub.utils import build_hf_headers +from datasets import disable_progress_bar +import sys +import contextlib +from concurrent.futures import ThreadPoolExecutor +import tempfile + +from app.config import ( + QUEUE_REPO, + HF_TOKEN, + EVAL_REQUESTS_PATH +) +from app.config.hf_config import HF_ORGANIZATION +from app.services.hf_service import HuggingFaceService +from app.utils.model_validation import ModelValidator +from app.services.votes import VoteService +from app.core.cache import cache_config +from app.utils.logging import LogFormatter + +# Disable datasets progress bars globally +disable_progress_bar() + +logger = logging.getLogger(__name__) + +# Context manager to temporarily disable stdout and stderr +@contextlib.contextmanager +def suppress_output(): + stdout = sys.stdout + stderr = sys.stderr + devnull = open(os.devnull, 'w') + try: + sys.stdout = devnull + sys.stderr = devnull + yield + finally: + sys.stdout = stdout + sys.stderr = stderr + devnull.close() + +class ProgressTracker: + def __init__(self, total: int, desc: str = "Progress", update_frequency: int = 10): + self.total = total + self.current = 0 + self.desc = desc + self.start_time = time.time() + self.update_frequency = update_frequency # Percentage steps + self.last_update = -1 + + # Initial log with fancy formatting + logger.info(LogFormatter.section(desc)) + logger.info(LogFormatter.info(f"Starting processing of {total:,} items...")) + sys.stdout.flush() + + def update(self, n: int = 1): + self.current += n + current_percentage = (self.current * 100) // self.total + + # Only update on frequency steps (e.g., 0%, 10%, 20%, etc.) + if current_percentage >= self.last_update + self.update_frequency or current_percentage == 100: + elapsed = time.time() - self.start_time + rate = self.current / elapsed if elapsed > 0 else 0 + remaining = (self.total - self.current) / rate if rate > 0 else 0 + + # Create progress stats + stats = { + "Progress": LogFormatter.progress_bar(self.current, self.total), + "Items": f"{self.current:,}/{self.total:,}", + "Time": f"⏱️ {elapsed:.1f}s elapsed, {remaining:.1f}s remaining", + "Rate": f"🚀 {rate:.1f} items/s" + } + + # Log progress using tree format + for line in LogFormatter.tree(stats): + logger.info(line) + sys.stdout.flush() + + self.last_update = (current_percentage // self.update_frequency) * self.update_frequency + + def close(self): + elapsed = time.time() - self.start_time + rate = self.total / elapsed if elapsed > 0 else 0 + + # Final summary with fancy formatting + logger.info(LogFormatter.section("COMPLETED")) + stats = { + "Total": f"{self.total:,} items", + "Time": f"{elapsed:.1f}s", + "Rate": f"{rate:.1f} items/s" + } + for line in LogFormatter.stats(stats): + logger.info(line) + logger.info("="*50) + sys.stdout.flush() + +class ModelService(HuggingFaceService): + _instance: Optional['ModelService'] = None + _initialized = False + + def __new__(cls): + if cls._instance is None: + logger.info(LogFormatter.info("Creating new ModelService instance")) + cls._instance = super(ModelService, cls).__new__(cls) + return cls._instance + + def __init__(self): + if not hasattr(self, '_init_done'): + logger.info(LogFormatter.section("MODEL SERVICE INITIALIZATION")) + super().__init__() + self.validator = ModelValidator() + self.vote_service = VoteService() + self.eval_requests_path = cache_config.eval_requests_file + logger.info(LogFormatter.info(f"Using eval requests path: {self.eval_requests_path}")) + + self.eval_requests_path.parent.mkdir(parents=True, exist_ok=True) + self.hf_api = HfApi(token=HF_TOKEN) + self.cached_models = None + self.last_cache_update = 0 + self.cache_ttl = cache_config.cache_ttl.total_seconds() + self._init_done = True + logger.info(LogFormatter.success("Initialization complete")) + + async def _download_and_process_file(self, file: str, session: aiohttp.ClientSession, progress: ProgressTracker) -> Optional[Dict]: + """Download and process a file asynchronously""" + try: + # Build file URL + url = f"https://huggingface.co/datasets/{QUEUE_REPO}/resolve/main/{file}" + headers = build_hf_headers(token=self.token) + + # Download file + async with session.get(url, headers=headers) as response: + if response.status != 200: + logger.error(LogFormatter.error(f"Failed to download {file}", f"HTTP {response.status}")) + progress.update() + return None + + try: + # First read content as text + text_content = await response.text() + # Then parse JSON + content = json.loads(text_content) + except json.JSONDecodeError as e: + logger.error(LogFormatter.error(f"Failed to decode JSON from {file}", e)) + progress.update() + return None + + # Get status and determine target status + status = content.get("status", "PENDING").upper() + target_status = None + status_map = { + "PENDING": ["PENDING"], + "EVALUATING": ["RUNNING"], + "FINISHED": ["FINISHED"] + } + + for target, source_statuses in status_map.items(): + if status in source_statuses: + target_status = target + break + + if not target_status: + progress.update() + return None + + # Calculate wait time + try: + submit_time = datetime.fromisoformat(content["submitted_time"].replace("Z", "+00:00")) + if submit_time.tzinfo is None: + submit_time = submit_time.replace(tzinfo=timezone.utc) + current_time = datetime.now(timezone.utc) + wait_time = current_time - submit_time + + model_info = { + "name": content["model"], + "submitter": content.get("sender", "Unknown"), + "revision": content["revision"], + "wait_time": f"{wait_time.total_seconds():.1f}s", + "submission_time": content["submitted_time"], + "status": target_status, + "precision": content.get("precision", "Unknown") + } + + progress.update() + return model_info + + except (ValueError, TypeError) as e: + logger.error(LogFormatter.error(f"Failed to process {file}", e)) + progress.update() + return None + + except Exception as e: + logger.error(LogFormatter.error(f"Failed to load {file}", e)) + progress.update() + return None + + async def _refresh_models_cache(self): + """Refresh the models cache""" + try: + logger.info(LogFormatter.section("CACHE REFRESH")) + self._log_repo_operation("read", f"{HF_ORGANIZATION}/requests", "Refreshing models cache") + + # Initialize models dictionary + models = { + "finished": [], + "evaluating": [], + "pending": [] + } + + try: + logger.info(LogFormatter.subsection("DATASET LOADING")) + logger.info(LogFormatter.info("Loading dataset files...")) + + # List files in repository + with suppress_output(): + files = self.hf_api.list_repo_files( + repo_id=QUEUE_REPO, + repo_type="dataset", + token=self.token + ) + + # Filter JSON files + json_files = [f for f in files if f.endswith('.json')] + total_files = len(json_files) + + # Log repository stats + stats = { + "Total_Files": len(files), + "JSON_Files": total_files, + } + for line in LogFormatter.stats(stats, "Repository Statistics"): + logger.info(line) + + if not json_files: + raise Exception("No JSON files found in repository") + + # Initialize progress tracker + progress = ProgressTracker(total_files, "PROCESSING FILES") + + try: + # Create aiohttp session to reuse connections + async with aiohttp.ClientSession() as session: + # Process files in chunks + chunk_size = 50 + + for i in range(0, len(json_files), chunk_size): + chunk = json_files[i:i + chunk_size] + chunk_tasks = [ + self._download_and_process_file(file, session, progress) + for file in chunk + ] + results = await asyncio.gather(*chunk_tasks) + + # Process results + for result in results: + if result: + status = result.pop("status") + models[status.lower()].append(result) + + finally: + progress.close() + + # Final summary with fancy formatting + logger.info(LogFormatter.section("CACHE SUMMARY")) + stats = { + "Finished": len(models["finished"]), + "Evaluating": len(models["evaluating"]), + "Pending": len(models["pending"]) + } + for line in LogFormatter.stats(stats, "Models by Status"): + logger.info(line) + logger.info("="*50) + + except Exception as e: + logger.error(LogFormatter.error("Error processing files", e)) + raise + + # Update cache + self.cached_models = models + self.last_cache_update = time.time() + logger.info(LogFormatter.success("Cache updated successfully")) + + return models + + except Exception as e: + logger.error(LogFormatter.error("Cache refresh failed", e)) + raise + + async def initialize(self): + """Initialize the model service""" + if self._initialized: + logger.info(LogFormatter.info("Service already initialized, using cached data")) + return + + try: + logger.info(LogFormatter.section("MODEL SERVICE INITIALIZATION")) + + # Check if cache already exists + cache_path = cache_config.get_cache_path("datasets") + if not cache_path.exists() or not any(cache_path.iterdir()): + logger.info(LogFormatter.info("No existing cache found, initializing datasets cache...")) + cache_config.flush_cache("datasets") + else: + logger.info(LogFormatter.info("Using existing datasets cache")) + + # Ensure eval requests directory exists + self.eval_requests_path.parent.mkdir(parents=True, exist_ok=True) + logger.info(LogFormatter.info(f"Eval requests directory: {self.eval_requests_path}")) + + # List existing files + if self.eval_requests_path.exists(): + files = list(self.eval_requests_path.glob("**/*.json")) + stats = { + "Total_Files": len(files), + "Directory": str(self.eval_requests_path) + } + for line in LogFormatter.stats(stats, "Eval Requests"): + logger.info(line) + + # Load initial cache + await self._refresh_models_cache() + + self._initialized = True + logger.info(LogFormatter.success("Model service initialization complete")) + + except Exception as e: + logger.error(LogFormatter.error("Initialization failed", e)) + raise + + async def get_models(self) -> Dict[str, List[Dict[str, Any]]]: + """Get all models with their status""" + if not self._initialized: + logger.info(LogFormatter.info("Service not initialized, initializing now...")) + await self.initialize() + + current_time = time.time() + cache_age = current_time - self.last_cache_update + + # Check if cache needs refresh + if not self.cached_models: + logger.info(LogFormatter.info("No cached data available, refreshing cache...")) + return await self._refresh_models_cache() + elif cache_age > self.cache_ttl: + logger.info(LogFormatter.info(f"Cache expired ({cache_age:.1f}s old, TTL: {self.cache_ttl}s)")) + return await self._refresh_models_cache() + else: + logger.info(LogFormatter.info(f"Using cached data ({cache_age:.1f}s old)")) + return self.cached_models + + async def submit_model( + self, + model_data: Dict[str, Any], + user_id: str + ) -> Dict[str, Any]: + logger.info(LogFormatter.section("MODEL SUBMISSION")) + self._log_repo_operation("write", f"{HF_ORGANIZATION}/requests", f"Submitting model {model_data['model_id']} by {user_id}") + stats = { + "Model": model_data["model_id"], + "User": user_id, + "Revision": model_data["revision"], + "Precision": model_data["precision"], + "Type": model_data["model_type"] + } + for line in LogFormatter.tree(stats, "Submission Details"): + logger.info(line) + + # Validate required fields + required_fields = [ + "model_id", "base_model", "revision", "precision", + "weight_type", "model_type", "use_chat_template" + ] + for field in required_fields: + if field not in model_data: + raise ValueError(f"Missing required field: {field}") + + # Get model info and validate it exists on HuggingFace + try: + logger.info(LogFormatter.subsection("MODEL VALIDATION")) + + # Get the model info to check if it exists + model_info = self.hf_api.model_info( + model_data["model_id"], + revision=model_data["revision"], + token=self.token + ) + + if not model_info: + raise Exception(f"Model {model_data['model_id']} not found on HuggingFace Hub") + + logger.info(LogFormatter.success("Model exists on HuggingFace Hub")) + + except Exception as e: + logger.error(LogFormatter.error("Model validation failed", e)) + raise + + # Update model revision with commit sha + model_data["revision"] = model_info.sha + + # Check if model already exists in the system + try: + logger.info(LogFormatter.subsection("CHECKING EXISTING SUBMISSIONS")) + existing_models = await self.get_models() + + # Check in all statuses (pending, evaluating, finished) + for status, models in existing_models.items(): + for model in models: + if model["name"] == model_data["model_id"] and model["revision"] == model_data["revision"]: + error_msg = f"Model {model_data['model_id']} revision {model_data['revision']} is already in the system with status: {status}" + logger.error(LogFormatter.error("Submission rejected", error_msg)) + raise ValueError(error_msg) + + logger.info(LogFormatter.success("No existing submission found")) + except ValueError: + raise + except Exception as e: + logger.error(LogFormatter.error("Failed to check existing submissions", e)) + raise + + # Check that model on hub and valid + valid, error, model_config = await self.validator.is_model_on_hub( + model_data["model_id"], + model_data["revision"], + test_tokenizer=True + ) + if not valid: + logger.error(LogFormatter.error("Model on hub validation failed", error)) + raise Exception(error) + logger.info(LogFormatter.success("Model on hub validation passed")) + + # Validate model card + valid, error, model_card = await self.validator.check_model_card( + model_data["model_id"] + ) + if not valid: + logger.error(LogFormatter.error("Model card validation failed", error)) + raise Exception(error) + logger.info(LogFormatter.success("Model card validation passed")) + + # Check size limits + model_size, error = await self.validator.get_model_size( + model_info, + model_data["precision"], + model_data["base_model"], + revision=model_data["revision"] + ) + if model_size is None: + logger.error(LogFormatter.error("Model size validation failed", error)) + raise Exception(error) + logger.info(LogFormatter.success(f"Model size validation passed: {model_size:.1f}B")) + + # Size limits based on precision + if model_data["precision"] in ["float16", "bfloat16"] and model_size > 100: + error_msg = f"Model too large for {model_data['precision']} (limit: 100B)" + logger.error(LogFormatter.error("Size limit exceeded", error_msg)) + raise Exception(error_msg) + + # Chat template validation if requested + if model_data["use_chat_template"]: + valid, error = await self.validator.check_chat_template( + model_data["model_id"], + model_data["revision"] + ) + if not valid: + logger.error(LogFormatter.error("Chat template validation failed", error)) + raise Exception(error) + logger.info(LogFormatter.success("Chat template validation passed")) + + + architectures = model_info.config.get("architectures", "") + if architectures: + architectures = ";".join(architectures) + + # Create eval entry + eval_entry = { + "model": model_data["model_id"], + "base_model": model_data["base_model"], + "revision": model_info.sha, + "precision": model_data["precision"], + "params": model_size, + "architectures": architectures, + "weight_type": model_data["weight_type"], + "status": "PENDING", + "submitted_time": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "model_type": model_data["model_type"], + "job_id": -1, + "job_start_time": None, + "use_chat_template": model_data["use_chat_template"], + "sender": user_id + } + + logger.info(LogFormatter.subsection("EVALUATION ENTRY")) + for line in LogFormatter.tree(eval_entry): + logger.info(line) + + # Upload to HF dataset + try: + logger.info(LogFormatter.subsection("UPLOADING TO HUGGINGFACE")) + logger.info(LogFormatter.info(f"Uploading to {HF_ORGANIZATION}/requests...")) + + # Construct the path in the dataset + org_or_user = model_data["model_id"].split("/")[0] if "/" in model_data["model_id"] else "" + model_path = model_data["model_id"].split("/")[-1] + relative_path = f"{org_or_user}/{model_path}_eval_request_False_{model_data['precision']}_{model_data['weight_type']}.json" + + # Create a temporary file with the request + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as temp_file: + json.dump(eval_entry, temp_file, indent=2) + temp_file.flush() + temp_path = temp_file.name + + # Upload file directly + self.hf_api.upload_file( + path_or_fileobj=temp_path, + path_in_repo=relative_path, + repo_id=f"{HF_ORGANIZATION}/requests", + repo_type="dataset", + commit_message=f"Add {model_data['model_id']} to eval queue", + token=self.token + ) + + # Clean up temp file + os.unlink(temp_path) + + logger.info(LogFormatter.success("Upload successful")) + + except Exception as e: + logger.error(LogFormatter.error("Upload failed", e)) + raise + + # Add automatic vote + try: + logger.info(LogFormatter.subsection("AUTOMATIC VOTE")) + logger.info(LogFormatter.info(f"Adding upvote for {model_data['model_id']} by {user_id}")) + await self.vote_service.add_vote( + model_data["model_id"], + user_id, + "up" + ) + logger.info(LogFormatter.success("Vote recorded successfully")) + except Exception as e: + logger.error(LogFormatter.error("Failed to record vote", e)) + # Don't raise here as the main submission was successful + + return { + "status": "success", + "message": "The model was submitted successfully, and the vote has been recorded" + } + + async def get_model_status(self, model_id: str) -> Dict[str, Any]: + """Get evaluation status of a model""" + logger.info(LogFormatter.info(f"Checking status for model: {model_id}")) + eval_path = self.eval_requests_path + + for user_folder in eval_path.iterdir(): + if user_folder.is_dir(): + for file in user_folder.glob("*.json"): + with open(file, "r") as f: + data = json.load(f) + if data["model"] == model_id: + status = { + "status": data["status"], + "submitted_time": data["submitted_time"], + "job_id": data.get("job_id", -1) + } + logger.info(LogFormatter.success("Status found")) + for line in LogFormatter.tree(status, "Model Status"): + logger.info(line) + return status + + logger.warning(LogFormatter.warning(f"No status found for model: {model_id}")) + return {"status": "not_found"} \ No newline at end of file diff --git a/backend/app/services/rate_limiter.py b/backend/app/services/rate_limiter.py new file mode 100644 index 0000000000000000000000000000000000000000..988c68e2f7d7f3847d6691c70f55975648aa3c8f --- /dev/null +++ b/backend/app/services/rate_limiter.py @@ -0,0 +1,72 @@ +""" +import logging +from datetime import datetime, timedelta, timezone +from typing import Tuple, Dict, List + +logger = logging.getLogger(__name__) + +class RateLimiter: + def __init__(self, period_days: int = 7, quota: int = 5): + self.period_days = period_days + self.quota = quota + self.submission_history: Dict[str, List[datetime]] = {} + self.higher_quota_users = set() # Users with higher quotas + self.unlimited_users = set() # Users with no quota limits + + def add_unlimited_user(self, user_id: str): + """Add a user to the unlimited users list""" + self.unlimited_users.add(user_id) + + def add_higher_quota_user(self, user_id: str): + """Add a user to the higher quota users list""" + self.higher_quota_users.add(user_id) + + def record_submission(self, user_id: str): + """Record a new submission for a user""" + current_time = datetime.now(timezone.utc) + if user_id not in self.submission_history: + self.submission_history[user_id] = [] + self.submission_history[user_id].append(current_time) + + def clean_old_submissions(self, user_id: str): + """Remove submissions older than the period""" + if user_id not in self.submission_history: + return + + current_time = datetime.now(timezone.utc) + cutoff_time = current_time - timedelta(days=self.period_days) + + self.submission_history[user_id] = [ + time for time in self.submission_history[user_id] + if time > cutoff_time + ] + + async def check_rate_limit(self, user_id: str) -> Tuple[bool, str]: + """Check if a user has exceeded their rate limit + + Returns: + Tuple[bool, str]: (is_allowed, error_message) + """ + # Unlimited users bypass all checks + if user_id in self.unlimited_users: + return True, "" + + # Clean old submissions + self.clean_old_submissions(user_id) + + # Get current submission count + submission_count = len(self.submission_history.get(user_id, [])) + + # Calculate user's quota + user_quota = self.quota * 2 if user_id in self.higher_quota_users else self.quota + + # Check if user has exceeded their quota + if submission_count >= user_quota: + error_msg = ( + f"User '{user_id}' has reached the limit of {user_quota} submissions " + f"in the last {self.period_days} days. Please wait before submitting again." + ) + return False, error_msg + + return True, "" +""" \ No newline at end of file diff --git a/backend/app/services/votes.py b/backend/app/services/votes.py new file mode 100644 index 0000000000000000000000000000000000000000..03ab371cf6d6c73d7dddacab1746b045ba65805e --- /dev/null +++ b/backend/app/services/votes.py @@ -0,0 +1,391 @@ +from datetime import datetime, timezone +from typing import Dict, Any, List, Set, Tuple, Optional +import json +import logging +import asyncio +from pathlib import Path +import os +import aiohttp +from huggingface_hub import HfApi +import datasets + +from app.services.hf_service import HuggingFaceService +from app.config import HF_TOKEN, API +from app.config.hf_config import HF_ORGANIZATION +from app.core.cache import cache_config +from app.utils.logging import LogFormatter + +logger = logging.getLogger(__name__) + +class VoteService(HuggingFaceService): + _instance: Optional['VoteService'] = None + _initialized = False + + def __new__(cls): + if cls._instance is None: + cls._instance = super(VoteService, cls).__new__(cls) + return cls._instance + + def __init__(self): + if not hasattr(self, '_init_done'): + super().__init__() + self.votes_file = cache_config.votes_file + self.votes_to_upload: List[Dict[str, Any]] = [] + self.vote_check_set: Set[Tuple[str, str, str]] = set() + self._votes_by_model: Dict[str, List[Dict[str, Any]]] = {} + self._votes_by_user: Dict[str, List[Dict[str, Any]]] = {} + self._upload_lock = asyncio.Lock() + self._last_sync = None + self._sync_interval = 300 # 5 minutes + self._total_votes = 0 + self._last_vote_timestamp = None + self._max_retries = 3 + self._retry_delay = 1 # seconds + self._upload_batch_size = 10 + self.hf_api = HfApi(token=HF_TOKEN) + self._init_done = True + + async def initialize(self): + """Initialize the vote service""" + if self._initialized: + await self._check_for_new_votes() + return + + try: + logger.info(LogFormatter.section("VOTE SERVICE INITIALIZATION")) + + # Ensure votes directory exists + self.votes_file.parent.mkdir(parents=True, exist_ok=True) + + # Load existing votes if file exists + local_vote_count = 0 + if self.votes_file.exists(): + logger.info(LogFormatter.info(f"Loading votes from {self.votes_file}")) + local_vote_count = await self._count_local_votes() + logger.info(LogFormatter.info(f"Found {local_vote_count:,} local votes")) + + # Check remote votes count + remote_vote_count = await self._count_remote_votes() + logger.info(LogFormatter.info(f"Found {remote_vote_count:,} remote votes")) + + if remote_vote_count > local_vote_count: + logger.info(LogFormatter.info(f"Fetching {remote_vote_count - local_vote_count:,} new votes")) + await self._sync_with_hub() + elif remote_vote_count < local_vote_count: + logger.warning(LogFormatter.warning(f"Local votes ({local_vote_count:,}) > Remote votes ({remote_vote_count:,})")) + await self._load_existing_votes() + else: + logger.info(LogFormatter.success("Local and remote votes are in sync")) + if local_vote_count > 0: + await self._load_existing_votes() + else: + logger.info(LogFormatter.info("No votes found")) + + self._initialized = True + self._last_sync = datetime.now(timezone.utc) + + # Final summary + stats = { + "Total_Votes": self._total_votes, + "Last_Sync": self._last_sync.strftime("%Y-%m-%d %H:%M:%S UTC") + } + logger.info(LogFormatter.section("INITIALIZATION COMPLETE")) + for line in LogFormatter.stats(stats): + logger.info(line) + + except Exception as e: + logger.error(LogFormatter.error("Initialization failed", e)) + raise + + async def _count_local_votes(self) -> int: + """Count votes in local file""" + if not self.votes_file.exists(): + return 0 + + count = 0 + try: + with open(self.votes_file, 'r') as f: + for _ in f: + count += 1 + return count + except Exception as e: + logger.error(f"Error counting local votes: {str(e)}") + return 0 + + async def _count_remote_votes(self) -> int: + """Count votes in remote file""" + url = f"https://huggingface.co/datasets/{HF_ORGANIZATION}/votes/raw/main/votes_data.jsonl" + headers = {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {} + + try: + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=headers) as response: + if response.status == 200: + count = 0 + async for line in response.content: + if line.strip(): # Skip empty lines + count += 1 + return count + else: + logger.error(f"Failed to get remote votes: HTTP {response.status}") + return 0 + except Exception as e: + logger.error(f"Error counting remote votes: {str(e)}") + return 0 + + async def _sync_with_hub(self): + """Sync votes with HuggingFace hub using datasets""" + try: + logger.info(LogFormatter.section("VOTE SYNC")) + self._log_repo_operation("sync", f"{HF_ORGANIZATION}/votes", "Syncing local votes with HF hub") + logger.info(LogFormatter.info("Syncing with HuggingFace hub...")) + + # Load votes from HF dataset + dataset = datasets.load_dataset( + f"{HF_ORGANIZATION}/votes", + split="train", + cache_dir=cache_config.get_cache_path("datasets") + ) + + remote_votes = len(dataset) + logger.info(LogFormatter.info(f"Dataset loaded with {remote_votes:,} votes")) + + # Convert to list of dictionaries + df = dataset.to_pandas() + if 'timestamp' in df.columns: + df['timestamp'] = df['timestamp'].dt.strftime('%Y-%m-%dT%H:%M:%SZ') + remote_votes = df.to_dict('records') + + # If we have more remote votes than local + if len(remote_votes) > self._total_votes: + new_votes = len(remote_votes) - self._total_votes + logger.info(LogFormatter.info(f"Processing {new_votes:,} new votes...")) + + # Save votes to local file + with open(self.votes_file, 'w') as f: + for vote in remote_votes: + f.write(json.dumps(vote) + '\n') + + # Reload votes in memory + await self._load_existing_votes() + logger.info(LogFormatter.success("Sync completed successfully")) + else: + logger.info(LogFormatter.success("Local votes are up to date")) + + self._last_sync = datetime.now(timezone.utc) + + except Exception as e: + logger.error(LogFormatter.error("Sync failed", e)) + raise + + async def _check_for_new_votes(self): + """Check for new votes on the hub""" + try: + self._log_repo_operation("check", f"{HF_ORGANIZATION}/votes", "Checking for new votes") + # Load only dataset metadata + dataset_info = datasets.load_dataset(f"{HF_ORGANIZATION}/votes", split="train") + remote_vote_count = len(dataset_info) + + if remote_vote_count > self._total_votes: + logger.info(f"Found {remote_vote_count - self._total_votes} new votes on hub") + await self._sync_with_hub() + else: + logger.info("No new votes found on hub") + + except Exception as e: + logger.error(f"Error checking for new votes: {str(e)}") + + async def _load_existing_votes(self): + """Load existing votes from file""" + if not self.votes_file.exists(): + logger.warning(LogFormatter.warning("No votes file found")) + return + + try: + logger.info(LogFormatter.section("LOADING VOTES")) + + # Clear existing data structures + self.vote_check_set.clear() + self._votes_by_model.clear() + self._votes_by_user.clear() + + vote_count = 0 + latest_timestamp = None + + with open(self.votes_file, "r") as f: + for line in f: + try: + vote = json.loads(line.strip()) + vote_count += 1 + + # Track latest timestamp + try: + vote_timestamp = datetime.fromisoformat(vote["timestamp"].replace("Z", "+00:00")) + if not latest_timestamp or vote_timestamp > latest_timestamp: + latest_timestamp = vote_timestamp + vote["timestamp"] = vote_timestamp.strftime("%Y-%m-%dT%H:%M:%SZ") + except (KeyError, ValueError) as e: + logger.warning(LogFormatter.warning(f"Invalid timestamp in vote: {str(e)}")) + continue + + if vote_count % 1000 == 0: + logger.info(LogFormatter.info(f"Processed {vote_count:,} votes...")) + + self._add_vote_to_memory(vote) + + except json.JSONDecodeError as e: + logger.error(LogFormatter.error("Vote parsing failed", e)) + continue + except Exception as e: + logger.error(LogFormatter.error("Vote processing failed", e)) + continue + + self._total_votes = vote_count + self._last_vote_timestamp = latest_timestamp + + # Final summary + stats = { + "Total_Votes": vote_count, + "Latest_Vote": latest_timestamp.strftime("%Y-%m-%d %H:%M:%S UTC") if latest_timestamp else "None", + "Unique_Models": len(self._votes_by_model), + "Unique_Users": len(self._votes_by_user) + } + + logger.info(LogFormatter.section("VOTE SUMMARY")) + for line in LogFormatter.stats(stats): + logger.info(line) + + except Exception as e: + logger.error(LogFormatter.error("Failed to load votes", e)) + raise + + def _add_vote_to_memory(self, vote: Dict[str, Any]): + """Add vote to memory structures""" + try: + check_tuple = (vote["model"], vote["revision"], vote["username"]) + + # Skip if we already have this vote + if check_tuple in self.vote_check_set: + return + + self.vote_check_set.add(check_tuple) + + # Update model votes + if vote["model"] not in self._votes_by_model: + self._votes_by_model[vote["model"]] = [] + self._votes_by_model[vote["model"]].append(vote) + + # Update user votes + if vote["username"] not in self._votes_by_user: + self._votes_by_user[vote["username"]] = [] + self._votes_by_user[vote["username"]].append(vote) + + except KeyError as e: + logger.error(f"Malformed vote data, missing key: {str(e)}") + except Exception as e: + logger.error(f"Error adding vote to memory: {str(e)}") + + async def get_user_votes(self, user_id: str) -> List[Dict[str, Any]]: + """Get all votes from a specific user""" + logger.info(LogFormatter.info(f"Fetching votes for user: {user_id}")) + votes = self._votes_by_user.get(user_id, []) + logger.info(LogFormatter.success(f"Found {len(votes):,} votes")) + return votes + + async def get_model_votes(self, model_id: str) -> Dict[str, Any]: + """Get all votes for a specific model""" + logger.info(LogFormatter.info(f"Fetching votes for model: {model_id}")) + votes = self._votes_by_model.get(model_id, []) + + # Group votes by revision + votes_by_revision = {} + for vote in votes: + revision = vote["revision"] + if revision not in votes_by_revision: + votes_by_revision[revision] = 0 + votes_by_revision[revision] += 1 + + stats = { + "Total_Votes": len(votes), + **{f"Revision_{k}": v for k, v in votes_by_revision.items()} + } + + logger.info(LogFormatter.section("VOTE STATISTICS")) + for line in LogFormatter.stats(stats): + logger.info(line) + + return { + "total_votes": len(votes), + "votes_by_revision": votes_by_revision, + "votes": votes + } + + async def _get_model_revision(self, model_id: str) -> str: + """Get current revision of a model with retries""" + logger.info(f"Getting revision for model: {model_id}") + for attempt in range(self._max_retries): + try: + model_info = await asyncio.to_thread(self.hf_api.model_info, model_id) + logger.info(f"Successfully got revision {model_info.sha} for model {model_id}") + return model_info.sha + except Exception as e: + logger.error(f"Error getting model revision for {model_id} (attempt {attempt + 1}): {str(e)}") + if attempt < self._max_retries - 1: + retry_delay = self._retry_delay * (attempt + 1) + logger.info(f"Retrying in {retry_delay} seconds...") + await asyncio.sleep(retry_delay) + else: + logger.warning(f"Using 'main' as fallback revision for {model_id} after {self._max_retries} failed attempts") + return "main" + + async def add_vote(self, model_id: str, user_id: str, vote_type: str) -> Dict[str, Any]: + """Add a vote for a model""" + try: + self._log_repo_operation("add", f"{HF_ORGANIZATION}/votes", f"Adding {vote_type} vote for {model_id} by {user_id}") + logger.info(LogFormatter.section("NEW VOTE")) + stats = { + "Model": model_id, + "User": user_id, + "Type": vote_type + } + for line in LogFormatter.tree(stats, "Vote Details"): + logger.info(line) + + revision = await self._get_model_revision(model_id) + check_tuple = (model_id, revision, user_id) + + if check_tuple in self.vote_check_set: + raise ValueError("Vote already recorded for this model") + + vote = { + "model": model_id, + "revision": revision, + "username": user_id, + "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "vote_type": vote_type + } + + # Update local storage + with open(self.votes_file, "a") as f: + f.write(json.dumps(vote) + "\n") + + self._add_vote_to_memory(vote) + self.votes_to_upload.append(vote) + + stats = { + "Status": "Success", + "Queue_Size": len(self.votes_to_upload) + } + for line in LogFormatter.stats(stats): + logger.info(line) + + # Try to upload if batch size reached + if len(self.votes_to_upload) >= self._upload_batch_size: + logger.info(LogFormatter.info(f"Upload batch size reached ({self._upload_batch_size}), triggering sync")) + await self._sync_with_hub() + + return {"status": "success", "message": "Vote added successfully"} + + except Exception as e: + logger.error(LogFormatter.error("Failed to add vote", e)) + raise \ No newline at end of file diff --git a/backend/app/utils/__init__.py b/backend/app/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..69a93acb760828c13400cfcd19da2822dfd83e5e --- /dev/null +++ b/backend/app/utils/__init__.py @@ -0,0 +1,3 @@ +from . import model_validation + +__all__ = ["model_validation"] diff --git a/backend/app/utils/logging.py b/backend/app/utils/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..f7e56730181304a799bdc61eae9a7b5d8a1c005a --- /dev/null +++ b/backend/app/utils/logging.py @@ -0,0 +1,105 @@ +import logging +import sys +from typing import Dict, Any, List, Optional + +logger = logging.getLogger(__name__) + +class LogFormatter: + """Utility class for consistent log formatting across the application""" + + @staticmethod + def section(title: str) -> str: + """Create a section header""" + return f"\n{'='*20} {title.upper()} {'='*20}" + + @staticmethod + def subsection(title: str) -> str: + """Create a subsection header""" + return f"\n{'─'*20} {title} {'─'*20}" + + @staticmethod + def tree(items: Dict[str, Any], title: str = None) -> List[str]: + """Create a tree view of dictionary data""" + lines = [] + if title: + lines.append(f"📊 {title}:") + + # Get the maximum length for alignment + max_key_length = max(len(str(k)) for k in items.keys()) + + # Format each item + for i, (key, value) in enumerate(items.items()): + prefix = "└──" if i == len(items) - 1 else "├──" + if isinstance(value, (int, float)): + value = f"{value:,}" # Add thousand separators + lines.append(f"{prefix} {str(key):<{max_key_length}}: {value}") + + return lines + + @staticmethod + def stats(stats: Dict[str, int], title: str = None) -> List[str]: + """Format statistics with icons""" + lines = [] + if title: + lines.append(f"📊 {title}:") + + # Get the maximum length for alignment + max_key_length = max(len(str(k)) for k in stats.keys()) + + # Format each stat with an appropriate icon + icons = { + "total": "📌", + "success": "✅", + "error": "❌", + "pending": "⏳", + "processing": "⚙️", + "finished": "✨", + "evaluating": "🔄", + "downloads": "⬇️", + "files": "📁", + "cached": "💾", + "size": "📏", + "time": "⏱️", + "rate": "🚀" + } + + # Format each item + for i, (key, value) in enumerate(stats.items()): + prefix = "└──" if i == len(stats) - 1 else "├──" + icon = icons.get(key.lower().split('_')[0], "•") + if isinstance(value, (int, float)): + value = f"{value:,}" # Add thousand separators + lines.append(f"{prefix} {icon} {str(key):<{max_key_length}}: {value}") + + return lines + + @staticmethod + def progress_bar(current: int, total: int, width: int = 20) -> str: + """Create a progress bar""" + percentage = (current * 100) // total + filled = "█" * (percentage * width // 100) + empty = "░" * (width - len(filled)) + return f"{filled}{empty} {percentage:3d}%" + + @staticmethod + def error(message: str, error: Optional[Exception] = None) -> str: + """Format error message""" + error_msg = f"\n❌ Error: {message}" + if error: + error_msg += f"\n └── Details: {str(error)}" + return error_msg + + @staticmethod + def success(message: str) -> str: + """Format success message""" + return f"✅ {message}" + + @staticmethod + def warning(message: str) -> str: + """Format warning message""" + return f"⚠️ {message}" + + @staticmethod + def info(message: str) -> str: + """Format info message""" + return f"ℹ️ {message}" \ No newline at end of file diff --git a/backend/app/utils/model_validation.py b/backend/app/utils/model_validation.py new file mode 100644 index 0000000000000000000000000000000000000000..4f51f06dfa1012e61bdc5016e0eb571021f46ef7 --- /dev/null +++ b/backend/app/utils/model_validation.py @@ -0,0 +1,210 @@ +import json +import logging +import asyncio +import re +from typing import Tuple, Optional, Dict, Any +import aiohttp +from huggingface_hub import HfApi, ModelCard, hf_hub_download +from huggingface_hub import hf_api +from transformers import AutoConfig, AutoTokenizer +from app.config.base import HF_TOKEN, API +from app.utils.logging import LogFormatter + + +logger = logging.getLogger(__name__) + +class ModelValidator: + def __init__(self): + self.token = HF_TOKEN + self.api = HfApi(token=self.token) + self.headers = {"Authorization": f"Bearer {self.token}"} if self.token else {} + + async def check_model_card(self, model_id: str) -> Tuple[bool, str, Optional[Dict[str, Any]]]: + """Check if model has a valid model card""" + try: + logger.info(LogFormatter.info(f"Checking model card for {model_id}")) + + # Get model card content using ModelCard.load + try: + model_card = await asyncio.to_thread( + ModelCard.load, + model_id + ) + logger.info(LogFormatter.success("Model card found")) + except Exception as e: + error_msg = "Please add a model card to your model to explain how you trained/fine-tuned it." + logger.error(LogFormatter.error(error_msg, e)) + return False, error_msg, None + + # Check license in model card data + if model_card.data.license is None and not ("license_name" in model_card.data and "license_link" in model_card.data): + error_msg = "License not found. Please add a license to your model card using the `license` metadata or a `license_name`/`license_link` pair." + logger.warning(LogFormatter.warning(error_msg)) + return False, error_msg, None + + # Enforce card content length + if len(model_card.text) < 200: + error_msg = "Please add a description to your model card, it is too short." + logger.warning(LogFormatter.warning(error_msg)) + return False, error_msg, None + + logger.info(LogFormatter.success("Model card validation passed")) + return True, "", model_card + + except Exception as e: + error_msg = "Failed to validate model card" + logger.error(LogFormatter.error(error_msg, e)) + return False, str(e), None + + async def get_safetensors_metadata(self, model_id: str, is_adapter: bool = False, revision: str = "main") -> Optional[Dict]: + """Get metadata from a safetensors file""" + try: + if is_adapter: + metadata = await asyncio.to_thread( + hf_api.parse_safetensors_file_metadata, + model_id, + "adapter_model.safetensors", + token=self.token, + revision=revision, + ) + else: + metadata = await asyncio.to_thread( + hf_api.get_safetensors_metadata, + repo_id=model_id, + token=self.token, + revision=revision, + ) + return metadata + + except Exception as e: + logger.error(f"Failed to get safetensors metadata: {str(e)}") + return None + + async def get_model_size( + self, + model_info: Any, + precision: str, + base_model: str, + revision: str + ) -> Tuple[Optional[float], Optional[str]]: + """Get model size in billions of parameters""" + try: + logger.info(LogFormatter.info(f"Checking model size for {model_info.modelId}")) + + # Check if model is adapter + is_adapter = any(s.rfilename == "adapter_config.json" for s in model_info.siblings if hasattr(s, 'rfilename')) + + # Try to get size from safetensors first + model_size = None + + if is_adapter and base_model: + # For adapters, we need both adapter and base model sizes + adapter_meta = await self.get_safetensors_metadata(model_info.id, is_adapter=True, revision=revision) + base_meta = await self.get_safetensors_metadata(base_model, revision="main") + + if adapter_meta and base_meta: + adapter_size = sum(adapter_meta.parameter_count.values()) + base_size = sum(base_meta.parameter_count.values()) + model_size = adapter_size + base_size + else: + # For regular models, just get the model size + meta = await self.get_safetensors_metadata(model_info.id, revision=revision) + if meta: + model_size = sum(meta.parameter_count.values()) # total params + + if model_size is None: + # If model size could not be determined, return an error + return None, "Model size could not be determined" + + # Adjust size for GPTQ models + size_factor = 8 if (precision == "GPTQ" or "gptq" in model_info.id.lower()) else 1 + model_size = model_size / 1e9 # Convert to billions, assuming float16 + model_size = round(size_factor * model_size, 3) + + logger.info(LogFormatter.success(f"Model size: {model_size}B parameters")) + return model_size, None + + except Exception as e: + logger.error(LogFormatter.error(f"Error while determining model size: {e}")) + return None, str(e) + + + async def check_chat_template( + self, + model_id: str, + revision: str + ) -> Tuple[bool, Optional[str]]: + """Check if model has a valid chat template""" + try: + logger.info(LogFormatter.info(f"Checking chat template for {model_id}")) + + try: + config_file = await asyncio.to_thread( + hf_hub_download, + repo_id=model_id, + filename="tokenizer_config.json", + revision=revision, + repo_type="model" + ) + + with open(config_file, 'r') as f: + tokenizer_config = json.load(f) + + if 'chat_template' not in tokenizer_config: + error_msg = f"The model {model_id} doesn't have a chat_template in its tokenizer_config.json. Please add a chat_template before submitting or submit without it." + logger.error(LogFormatter.error(error_msg)) + return False, error_msg + + logger.info(LogFormatter.success("Valid chat template found")) + return True, None + + except Exception as e: + error_msg = f"Error checking chat_template: {str(e)}" + logger.error(LogFormatter.error(error_msg)) + return False, error_msg + + except Exception as e: + error_msg = "Failed to check chat template" + logger.error(LogFormatter.error(error_msg, e)) + return False, str(e) + + async def is_model_on_hub( + self, + model_name: str, + revision: str, + test_tokenizer: bool = False, + trust_remote_code: bool = False + ) -> Tuple[bool, Optional[str], Optional[Any]]: + """Check if model exists and is properly configured on the Hub""" + try: + config = await asyncio.to_thread( + AutoConfig.from_pretrained, + model_name, + revision=revision, + trust_remote_code=trust_remote_code, + token=self.token, + force_download=True + ) + + if test_tokenizer: + try: + await asyncio.to_thread( + AutoTokenizer.from_pretrained, + model_name, + revision=revision, + trust_remote_code=trust_remote_code, + token=self.token + ) + except ValueError as e: + return False, f"The tokenizer is not available in an official Transformers release: {e}", None + except Exception: + return False, "The tokenizer cannot be loaded. Ensure the tokenizer class is part of a stable Transformers release and correctly configured.", None + + return True, None, config + + except ValueError: + return False, "The model requires `trust_remote_code=True` to launch, and for safety reasons, we don't accept such models automatically.", None + except Exception as e: + if "You are trying to access a gated repo." in str(e): + return True, "The model is gated and requires special access permissions.", None + return False, f"The model was not found or is misconfigured on the Hub. Error: {e.args[0]}", None \ No newline at end of file diff --git a/backend/poetry.lock b/backend/poetry.lock new file mode 100644 index 0000000000000000000000000000000000000000..cf8e21986664202d44f2f2761c3d3815b5db405a --- /dev/null +++ b/backend/poetry.lock @@ -0,0 +1,2651 @@ +# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. + +[[package]] +name = "aiofiles" +version = "24.1.0" +description = "File support for asyncio." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, + {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.4" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, + {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, +] + +[[package]] +name = "aiohttp" +version = "3.11.11" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, + {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, + {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, + {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, + {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, + {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, + {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, + {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, + {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, + {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, + {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.2" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.8.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, + {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "attrs" +version = "24.3.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.12.14" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "datasets" +version = "3.2.0" +description = "HuggingFace community-driven open-source library of datasets" +optional = false +python-versions = ">=3.9.0" +groups = ["main"] +files = [ + {file = "datasets-3.2.0-py3-none-any.whl", hash = "sha256:f3d2ba2698b7284a4518019658596a6a8bc79f31e51516524249d6c59cf0fe2a"}, + {file = "datasets-3.2.0.tar.gz", hash = "sha256:9a6e1a356052866b5dbdd9c9eedb000bf3fc43d986e3584d9b028f4976937229"}, +] + +[package.dependencies] +aiohttp = "*" +dill = ">=0.3.0,<0.3.9" +filelock = "*" +fsspec = {version = ">=2023.1.0,<=2024.9.0", extras = ["http"]} +huggingface-hub = ">=0.23.0" +multiprocess = "<0.70.17" +numpy = ">=1.17" +packaging = "*" +pandas = "*" +pyarrow = ">=15.0.0" +pyyaml = ">=5.1" +requests = ">=2.32.2" +tqdm = ">=4.66.3" +xxhash = "*" + +[package.extras] +audio = ["librosa", "soundfile (>=0.12.1)", "soxr (>=0.4.0)"] +benchmarks = ["tensorflow (==2.12.0)", "torch (==2.0.1)", "transformers (==4.30.1)"] +dev = ["Pillow (>=9.4.0)", "absl-py", "decorator", "decord (==0.6.0)", "elasticsearch (>=7.17.12,<8.0.0)", "faiss-cpu (>=1.8.0.post1)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "moto[server]", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "ruff (>=0.3.0)", "s3fs", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "soxr (>=0.4.0)", "sqlalchemy", "tensorflow (>=2.16.0)", "tensorflow (>=2.6.0)", "tensorflow (>=2.6.0)", "tiktoken", "torch", "torch (>=2.0.0)", "torchdata", "transformers", "transformers (>=4.42.0)", "zstandard"] +docs = ["s3fs", "tensorflow (>=2.6.0)", "torch", "transformers"] +jax = ["jax (>=0.3.14)", "jaxlib (>=0.3.14)"] +quality = ["ruff (>=0.3.0)"] +s3 = ["s3fs"] +tensorflow = ["tensorflow (>=2.6.0)"] +tensorflow-gpu = ["tensorflow (>=2.6.0)"] +tests = ["Pillow (>=9.4.0)", "absl-py", "decorator", "decord (==0.6.0)", "elasticsearch (>=7.17.12,<8.0.0)", "faiss-cpu (>=1.8.0.post1)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "moto[server]", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "soxr (>=0.4.0)", "sqlalchemy", "tensorflow (>=2.16.0)", "tensorflow (>=2.6.0)", "tiktoken", "torch (>=2.0.0)", "torchdata", "transformers (>=4.42.0)", "zstandard"] +tests-numpy2 = ["Pillow (>=9.4.0)", "absl-py", "decorator", "decord (==0.6.0)", "elasticsearch (>=7.17.12,<8.0.0)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "lz4", "moto[server]", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "soxr (>=0.4.0)", "sqlalchemy", "tiktoken", "torch (>=2.0.0)", "torchdata", "transformers (>=4.42.0)", "zstandard"] +torch = ["torch"] +vision = ["Pillow (>=9.4.0)"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "fastapi" +version = "0.115.6" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"}, + {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.42.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cache2" +version = "0.2.2" +description = "Cache for FastAPI" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "fastapi_cache2-0.2.2-py3-none-any.whl", hash = "sha256:e1fae86d8eaaa6c8501dfe08407f71d69e87cc6748042d59d51994000532846c"}, + {file = "fastapi_cache2-0.2.2.tar.gz", hash = "sha256:71bf4450117dc24224ec120be489dbe09e331143c9f74e75eb6f576b78926026"}, +] + +[package.dependencies] +fastapi = "*" +pendulum = ">=3.0.0,<4.0.0" +typing-extensions = ">=4.1.0" +uvicorn = "*" + +[package.extras] +all = ["aiobotocore (>=2.13.1,<3.0.0)", "aiomcache (>=0.8.2,<0.9.0)", "redis (>=4.2.0rc1,<5.0.0)"] +dynamodb = ["aiobotocore (>=2.13.1,<3.0.0)"] +memcache = ["aiomcache (>=0.8.2,<0.9.0)"] +redis = ["redis (>=4.2.0rc1,<5.0.0)"] + +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +groups = ["dev"] +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + +[[package]] +name = "frozenlist" +version = "1.5.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, +] + +[[package]] +name = "fsspec" +version = "2024.9.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, + {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, +] + +[package.dependencies] +aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""} + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httptools" +version = "0.6.4" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, + {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4"}, + {file = "httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988"}, + {file = "httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f"}, + {file = "httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0"}, + {file = "httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d3f0d369e7ffbe59c4b6116a44d6a8eb4783aae027f2c0b366cf0aa964185dba"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:94978a49b8f4569ad607cd4946b759d90b285e39c0d4640c6b36ca7a3ddf2efc"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dc6a8e399e15ea525305a2ddba998b0af5caa2566bcd79dcbe8948181eeaff"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab9ba8dcf59de5181f6be44a77458e45a578fc99c31510b8c65b7d5acc3cf490"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc411e1c0a7dcd2f902c7c48cf079947a7e65b5485dea9decb82b9105ca71a43"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d54efd20338ac52ba31e7da78e4a72570cf729fac82bc31ff9199bedf1dc7440"}, + {file = "httptools-0.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:df959752a0c2748a65ab5387d08287abf6779ae9165916fe053e68ae1fbdc47f"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd"}, + {file = "httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6"}, + {file = "httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c"}, +] + +[package.extras] +test = ["Cython (>=0.29.24)"] + +[[package]] +name = "huggingface-hub" +version = "0.27.1" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "huggingface_hub-0.27.1-py3-none-any.whl", hash = "sha256:1c5155ca7d60b60c2e2fc38cbb3ffb7f7c3adf48f824015b219af9061771daec"}, + {file = "huggingface_hub-0.27.1.tar.gz", hash = "sha256:c004463ca870283909d715d20f066ebd6968c2207dae9393fdffb3c1d4d8f98b"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp"] +quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +groups = ["dev"] +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +description = "better multiprocessing and multithreading in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"}, + {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"}, + {file = "multiprocess-0.70.16-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37b55f71c07e2d741374998c043b9520b626a8dddc8b3129222ca4f1a06ef67a"}, + {file = "multiprocess-0.70.16-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba8c31889abf4511c7308a8c52bb4a30b9d590e7f58523302ba00237702ca054"}, + {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"}, + {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"}, + {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"}, + {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"}, + {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"}, + {file = "multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435"}, + {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"}, + {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"}, +] + +[package.dependencies] +dill = ">=0.3.8" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "2.2.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "numpy-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308"}, + {file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957"}, + {file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf"}, + {file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2"}, + {file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528"}, + {file = "numpy-2.2.1-cp310-cp310-win32.whl", hash = "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95"}, + {file = "numpy-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5"}, + {file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73"}, + {file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591"}, + {file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8"}, + {file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0"}, + {file = "numpy-2.2.1-cp311-cp311-win32.whl", hash = "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd"}, + {file = "numpy-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355"}, + {file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7"}, + {file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d"}, + {file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51"}, + {file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046"}, + {file = "numpy-2.2.1-cp312-cp312-win32.whl", hash = "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2"}, + {file = "numpy-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348"}, + {file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59"}, + {file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af"}, + {file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51"}, + {file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716"}, + {file = "numpy-2.2.1-cp313-cp313-win32.whl", hash = "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e"}, + {file = "numpy-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84"}, + {file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631"}, + {file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d"}, + {file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5"}, + {file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71"}, + {file = "numpy-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2"}, + {file = "numpy-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e"}, + {file = "numpy-2.2.1.tar.gz", hash = "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = {version = ">=1.26.0", markers = "python_version >= \"3.12\""} +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pendulum" +version = "3.0.0" +description = "Python datetimes made easy" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pendulum-3.0.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2cf9e53ef11668e07f73190c805dbdf07a1939c3298b78d5a9203a86775d1bfd"}, + {file = "pendulum-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fb551b9b5e6059377889d2d878d940fd0bbb80ae4810543db18e6f77b02c5ef6"}, + {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c58227ac260d5b01fc1025176d7b31858c9f62595737f350d22124a9a3ad82d"}, + {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60fb6f415fea93a11c52578eaa10594568a6716602be8430b167eb0d730f3332"}, + {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b69f6b4dbcb86f2c2fe696ba991e67347bcf87fe601362a1aba6431454b46bde"}, + {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:138afa9c373ee450ede206db5a5e9004fd3011b3c6bbe1e57015395cd076a09f"}, + {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:83d9031f39c6da9677164241fd0d37fbfc9dc8ade7043b5d6d62f56e81af8ad2"}, + {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c2308af4033fa534f089595bcd40a95a39988ce4059ccd3dc6acb9ef14ca44a"}, + {file = "pendulum-3.0.0-cp310-none-win_amd64.whl", hash = "sha256:9a59637cdb8462bdf2dbcb9d389518c0263799189d773ad5c11db6b13064fa79"}, + {file = "pendulum-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3725245c0352c95d6ca297193192020d1b0c0f83d5ee6bb09964edc2b5a2d508"}, + {file = "pendulum-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6c035f03a3e565ed132927e2c1b691de0dbf4eb53b02a5a3c5a97e1a64e17bec"}, + {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597e66e63cbd68dd6d58ac46cb7a92363d2088d37ccde2dae4332ef23e95cd00"}, + {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99a0f8172e19f3f0c0e4ace0ad1595134d5243cf75985dc2233e8f9e8de263ca"}, + {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77d8839e20f54706aed425bec82a83b4aec74db07f26acd039905d1237a5e1d4"}, + {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afde30e8146292b059020fbc8b6f8fd4a60ae7c5e6f0afef937bbb24880bdf01"}, + {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:660434a6fcf6303c4efd36713ca9212c753140107ee169a3fc6c49c4711c2a05"}, + {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dee9e5a48c6999dc1106eb7eea3e3a50e98a50651b72c08a87ee2154e544b33e"}, + {file = "pendulum-3.0.0-cp311-none-win_amd64.whl", hash = "sha256:d4cdecde90aec2d67cebe4042fd2a87a4441cc02152ed7ed8fb3ebb110b94ec4"}, + {file = "pendulum-3.0.0-cp311-none-win_arm64.whl", hash = "sha256:773c3bc4ddda2dda9f1b9d51fe06762f9200f3293d75c4660c19b2614b991d83"}, + {file = "pendulum-3.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:409e64e41418c49f973d43a28afe5df1df4f1dd87c41c7c90f1a63f61ae0f1f7"}, + {file = "pendulum-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a38ad2121c5ec7c4c190c7334e789c3b4624798859156b138fcc4d92295835dc"}, + {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fde4d0b2024b9785f66b7f30ed59281bd60d63d9213cda0eb0910ead777f6d37"}, + {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2c5675769fb6d4c11238132962939b960fcb365436b6d623c5864287faa319"}, + {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8af95e03e066826f0f4c65811cbee1b3123d4a45a1c3a2b4fc23c4b0dff893b5"}, + {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2165a8f33cb15e06c67070b8afc87a62b85c5a273e3aaa6bc9d15c93a4920d6f"}, + {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ad5e65b874b5e56bd942546ea7ba9dd1d6a25121db1c517700f1c9de91b28518"}, + {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17fe4b2c844bbf5f0ece69cfd959fa02957c61317b2161763950d88fed8e13b9"}, + {file = "pendulum-3.0.0-cp312-none-win_amd64.whl", hash = "sha256:78f8f4e7efe5066aca24a7a57511b9c2119f5c2b5eb81c46ff9222ce11e0a7a5"}, + {file = "pendulum-3.0.0-cp312-none-win_arm64.whl", hash = "sha256:28f49d8d1e32aae9c284a90b6bb3873eee15ec6e1d9042edd611b22a94ac462f"}, + {file = "pendulum-3.0.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d4e2512f4e1a4670284a153b214db9719eb5d14ac55ada5b76cbdb8c5c00399d"}, + {file = "pendulum-3.0.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:3d897eb50883cc58d9b92f6405245f84b9286cd2de6e8694cb9ea5cb15195a32"}, + {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e169cc2ca419517f397811bbe4589cf3cd13fca6dc38bb352ba15ea90739ebb"}, + {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17c3084a4524ebefd9255513692f7e7360e23c8853dc6f10c64cc184e1217ab"}, + {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:826d6e258052715f64d05ae0fc9040c0151e6a87aae7c109ba9a0ed930ce4000"}, + {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2aae97087872ef152a0c40e06100b3665d8cb86b59bc8471ca7c26132fccd0f"}, + {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ac65eeec2250d03106b5e81284ad47f0d417ca299a45e89ccc69e36130ca8bc7"}, + {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5346d08f3f4a6e9e672187faa179c7bf9227897081d7121866358af369f44f9"}, + {file = "pendulum-3.0.0-cp37-none-win_amd64.whl", hash = "sha256:235d64e87946d8f95c796af34818c76e0f88c94d624c268693c85b723b698aa9"}, + {file = "pendulum-3.0.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a881d9c2a7f85bc9adafcfe671df5207f51f5715ae61f5d838b77a1356e8b7b"}, + {file = "pendulum-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7762d2076b9b1cb718a6631ad6c16c23fc3fac76cbb8c454e81e80be98daa34"}, + {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8e36a8130819d97a479a0e7bf379b66b3b1b520e5dc46bd7eb14634338df8c"}, + {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dc843253ac373358ffc0711960e2dd5b94ab67530a3e204d85c6e8cb2c5fa10"}, + {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a78ad3635d609ceb1e97d6aedef6a6a6f93433ddb2312888e668365908c7120"}, + {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a137e9e0d1f751e60e67d11fc67781a572db76b2296f7b4d44554761049d6"}, + {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c95984037987f4a457bb760455d9ca80467be792236b69d0084f228a8ada0162"}, + {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d29c6e578fe0f893766c0d286adbf0b3c726a4e2341eba0917ec79c50274ec16"}, + {file = "pendulum-3.0.0-cp38-none-win_amd64.whl", hash = "sha256:deaba8e16dbfcb3d7a6b5fabdd5a38b7c982809567479987b9c89572df62e027"}, + {file = "pendulum-3.0.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b11aceea5b20b4b5382962b321dbc354af0defe35daa84e9ff3aae3c230df694"}, + {file = "pendulum-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a90d4d504e82ad236afac9adca4d6a19e4865f717034fc69bafb112c320dcc8f"}, + {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:825799c6b66e3734227756fa746cc34b3549c48693325b8b9f823cb7d21b19ac"}, + {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad769e98dc07972e24afe0cff8d365cb6f0ebc7e65620aa1976fcfbcadc4c6f3"}, + {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6fc26907eb5fb8cc6188cc620bc2075a6c534d981a2f045daa5f79dfe50d512"}, + {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c717eab1b6d898c00a3e0fa7781d615b5c5136bbd40abe82be100bb06df7a56"}, + {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3ddd1d66d1a714ce43acfe337190be055cdc221d911fc886d5a3aae28e14b76d"}, + {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:822172853d7a9cf6da95d7b66a16c7160cb99ae6df55d44373888181d7a06edc"}, + {file = "pendulum-3.0.0-cp39-none-win_amd64.whl", hash = "sha256:840de1b49cf1ec54c225a2a6f4f0784d50bd47f68e41dc005b7f67c7d5b5f3ae"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b1f74d1e6ffe5d01d6023870e2ce5c2191486928823196f8575dcc786e107b1"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:729e9f93756a2cdfa77d0fc82068346e9731c7e884097160603872686e570f07"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e586acc0b450cd21cbf0db6bae386237011b75260a3adceddc4be15334689a9a"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22e7944ffc1f0099a79ff468ee9630c73f8c7835cd76fdb57ef7320e6a409df4"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa30af36bd8e50686846bdace37cf6707bdd044e5cb6e1109acbad3277232e04"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:440215347b11914ae707981b9a57ab9c7b6983ab0babde07063c6ee75c0dc6e7"}, + {file = "pendulum-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:314c4038dc5e6a52991570f50edb2f08c339debdf8cea68ac355b32c4174e820"}, + {file = "pendulum-3.0.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5acb1d386337415f74f4d1955c4ce8d0201978c162927d07df8eb0692b2d8533"}, + {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a789e12fbdefaffb7b8ac67f9d8f22ba17a3050ceaaa635cd1cc4645773a4b1e"}, + {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:860aa9b8a888e5913bd70d819306749e5eb488e6b99cd6c47beb701b22bdecf5"}, + {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5ebc65ea033ef0281368217fbf59f5cb05b338ac4dd23d60959c7afcd79a60a0"}, + {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9fef18ab0386ef6a9ac7bad7e43ded42c83ff7ad412f950633854f90d59afa8"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c134ba2f0571d0b68b83f6972e2307a55a5a849e7dac8505c715c531d2a8795"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:385680812e7e18af200bb9b4a49777418c32422d05ad5a8eb85144c4a285907b"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eec91cd87c59fb32ec49eb722f375bd58f4be790cae11c1b70fac3ee4f00da0"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4386bffeca23c4b69ad50a36211f75b35a4deb6210bdca112ac3043deb7e494a"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dfbcf1661d7146d7698da4b86e7f04814221081e9fe154183e34f4c5f5fa3bf8"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:04a1094a5aa1daa34a6b57c865b25f691848c61583fb22722a4df5699f6bf74c"}, + {file = "pendulum-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5b0ec85b9045bd49dd3a3493a5e7ddfd31c36a2a60da387c419fa04abcaecb23"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0a15b90129765b705eb2039062a6daf4d22c4e28d1a54fa260892e8c3ae6e157"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:bb8f6d7acd67a67d6fedd361ad2958ff0539445ef51cbe8cd288db4306503cd0"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd69b15374bef7e4b4440612915315cc42e8575fcda2a3d7586a0d88192d0c88"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc00f8110db6898360c53c812872662e077eaf9c75515d53ecc65d886eec209a"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83a44e8b40655d0ba565a5c3d1365d27e3e6778ae2a05b69124db9e471255c4a"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1a3604e9fbc06b788041b2a8b78f75c243021e0f512447806a6d37ee5214905d"}, + {file = "pendulum-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:92c307ae7accebd06cbae4729f0ba9fa724df5f7d91a0964b1b972a22baa482b"}, + {file = "pendulum-3.0.0.tar.gz", hash = "sha256:5d034998dea404ec31fae27af6b22cff1708f830a1ed7353be4d1019bb9f584e"}, +] + +[package.dependencies] +python-dateutil = ">=2.6" +tzdata = ">=2020.1" + +[package.extras] +test = ["time-machine (>=2.6.0)"] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "propcache" +version = "0.2.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, +] + +[[package]] +name = "pyarrow" +version = "18.1.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c"}, + {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56"}, + {file = "pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812"}, + {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854"}, + {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0"}, + {file = "pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a"}, + {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d"}, + {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30"}, + {file = "pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99"}, + {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b"}, + {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c"}, + {file = "pyarrow-18.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181"}, + {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc"}, + {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba"}, + {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:0b331e477e40f07238adc7ba7469c36b908f07c89b95dd4bd3a0ec84a3d1e21e"}, + {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c4dd0c9010a25ba03e198fe743b1cc03cd33c08190afff371749c52ccbbaf76"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f97b31b4c4e21ff58c6f330235ff893cc81e23da081b1a4b1c982075e0ed4e9"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a4813cb8ecf1809871fd2d64a8eff740a1bd3691bbe55f01a3cf6c5ec869754"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:05a5636ec3eb5cc2a36c6edb534a38ef57b2ab127292a716d00eabb887835f1e"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:73eeed32e724ea3568bb06161cad5fa7751e45bc2228e33dcb10c614044165c7"}, + {file = "pyarrow-18.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a1880dd6772b685e803011a6b43a230c23b566859a6e0c9a276c1e0faf4f4052"}, + {file = "pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73"}, +] + +[package.extras] +test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pydantic" +version = "2.10.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, + {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.20" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, + {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, +] + +[[package]] +name = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "regex" +version = "2024.11.6" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "safetensors" +version = "0.4.5" +description = "" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "safetensors-0.4.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a63eaccd22243c67e4f2b1c3e258b257effc4acd78f3b9d397edc8cf8f1298a7"}, + {file = "safetensors-0.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:23fc9b4ec7b602915cbb4ec1a7c1ad96d2743c322f20ab709e2c35d1b66dad27"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6885016f34bef80ea1085b7e99b3c1f92cb1be78a49839203060f67b40aee761"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:133620f443450429322f238fda74d512c4008621227fccf2f8cf4a76206fea7c"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4fb3e0609ec12d2a77e882f07cced530b8262027f64b75d399f1504ffec0ba56"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0f1dd769f064adc33831f5e97ad07babbd728427f98e3e1db6902e369122737"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6d156bdb26732feada84f9388a9f135528c1ef5b05fae153da365ad4319c4c5"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e347d77e2c77eb7624400ccd09bed69d35c0332f417ce8c048d404a096c593b"}, + {file = "safetensors-0.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9f556eea3aec1d3d955403159fe2123ddd68e880f83954ee9b4a3f2e15e716b6"}, + {file = "safetensors-0.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9483f42be3b6bc8ff77dd67302de8ae411c4db39f7224dec66b0eb95822e4163"}, + {file = "safetensors-0.4.5-cp310-none-win32.whl", hash = "sha256:7389129c03fadd1ccc37fd1ebbc773f2b031483b04700923c3511d2a939252cc"}, + {file = "safetensors-0.4.5-cp310-none-win_amd64.whl", hash = "sha256:e98ef5524f8b6620c8cdef97220c0b6a5c1cef69852fcd2f174bb96c2bb316b1"}, + {file = "safetensors-0.4.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:21f848d7aebd5954f92538552d6d75f7c1b4500f51664078b5b49720d180e47c"}, + {file = "safetensors-0.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb07000b19d41e35eecef9a454f31a8b4718a185293f0d0b1c4b61d6e4487971"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09dedf7c2fda934ee68143202acff6e9e8eb0ddeeb4cfc24182bef999efa9f42"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59b77e4b7a708988d84f26de3ebead61ef1659c73dcbc9946c18f3b1786d2688"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d3bc83e14d67adc2e9387e511097f254bd1b43c3020440e708858c684cbac68"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39371fc551c1072976073ab258c3119395294cf49cdc1f8476794627de3130df"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6c19feda32b931cae0acd42748a670bdf56bee6476a046af20181ad3fee4090"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a659467495de201e2f282063808a41170448c78bada1e62707b07a27b05e6943"}, + {file = "safetensors-0.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bad5e4b2476949bcd638a89f71b6916fa9a5cae5c1ae7eede337aca2100435c0"}, + {file = "safetensors-0.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a3a315a6d0054bc6889a17f5668a73f94f7fe55121ff59e0a199e3519c08565f"}, + {file = "safetensors-0.4.5-cp311-none-win32.whl", hash = "sha256:a01e232e6d3d5cf8b1667bc3b657a77bdab73f0743c26c1d3c5dd7ce86bd3a92"}, + {file = "safetensors-0.4.5-cp311-none-win_amd64.whl", hash = "sha256:cbd39cae1ad3e3ef6f63a6f07296b080c951f24cec60188378e43d3713000c04"}, + {file = "safetensors-0.4.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:473300314e026bd1043cef391bb16a8689453363381561b8a3e443870937cc1e"}, + {file = "safetensors-0.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:801183a0f76dc647f51a2d9141ad341f9665602a7899a693207a82fb102cc53e"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1524b54246e422ad6fb6aea1ac71edeeb77666efa67230e1faf6999df9b2e27f"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3139098e3e8b2ad7afbca96d30ad29157b50c90861084e69fcb80dec7430461"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65573dc35be9059770808e276b017256fa30058802c29e1038eb1c00028502ea"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd33da8e9407559f8779c82a0448e2133737f922d71f884da27184549416bfed"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3685ce7ed036f916316b567152482b7e959dc754fcc4a8342333d222e05f407c"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dde2bf390d25f67908278d6f5d59e46211ef98e44108727084d4637ee70ab4f1"}, + {file = "safetensors-0.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7469d70d3de970b1698d47c11ebbf296a308702cbaae7fcb993944751cf985f4"}, + {file = "safetensors-0.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a6ba28118636a130ccbb968bc33d4684c48678695dba2590169d5ab03a45646"}, + {file = "safetensors-0.4.5-cp312-none-win32.whl", hash = "sha256:c859c7ed90b0047f58ee27751c8e56951452ed36a67afee1b0a87847d065eec6"}, + {file = "safetensors-0.4.5-cp312-none-win_amd64.whl", hash = "sha256:b5a8810ad6a6f933fff6c276eae92c1da217b39b4d8b1bc1c0b8af2d270dc532"}, + {file = "safetensors-0.4.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:25e5f8e2e92a74f05b4ca55686234c32aac19927903792b30ee6d7bd5653d54e"}, + {file = "safetensors-0.4.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81efb124b58af39fcd684254c645e35692fea81c51627259cdf6d67ff4458916"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:585f1703a518b437f5103aa9cf70e9bd437cb78eea9c51024329e4fb8a3e3679"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b99fbf72e3faf0b2f5f16e5e3458b93b7d0a83984fe8d5364c60aa169f2da89"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b17b299ca9966ca983ecda1c0791a3f07f9ca6ab5ded8ef3d283fff45f6bcd5f"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76ded72f69209c9780fdb23ea89e56d35c54ae6abcdec67ccb22af8e696e449a"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2783956926303dcfeb1de91a4d1204cd4089ab441e622e7caee0642281109db3"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d94581aab8c6b204def4d7320f07534d6ee34cd4855688004a4354e63b639a35"}, + {file = "safetensors-0.4.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:67e1e7cb8678bb1b37ac48ec0df04faf689e2f4e9e81e566b5c63d9f23748523"}, + {file = "safetensors-0.4.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbd280b07e6054ea68b0cb4b16ad9703e7d63cd6890f577cb98acc5354780142"}, + {file = "safetensors-0.4.5-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:77d9b228da8374c7262046a36c1f656ba32a93df6cc51cd4453af932011e77f1"}, + {file = "safetensors-0.4.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:500cac01d50b301ab7bb192353317035011c5ceeef0fca652f9f43c000bb7f8d"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75331c0c746f03158ded32465b7d0b0e24c5a22121743662a2393439c43a45cf"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670e95fe34e0d591d0529e5e59fd9d3d72bc77b1444fcaa14dccda4f36b5a38b"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:098923e2574ff237c517d6e840acada8e5b311cb1fa226019105ed82e9c3b62f"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ca0902d2648775089fa6a0c8fc9e6390c5f8ee576517d33f9261656f851e3f"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0032bedc869c56f8d26259fe39cd21c5199cd57f2228d817a0e23e8370af25"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4b15f51b4f8f2a512341d9ce3475cacc19c5fdfc5db1f0e19449e75f95c7dc8"}, + {file = "safetensors-0.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f6594d130d0ad933d885c6a7b75c5183cb0e8450f799b80a39eae2b8508955eb"}, + {file = "safetensors-0.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:60c828a27e852ded2c85fc0f87bf1ec20e464c5cd4d56ff0e0711855cc2e17f8"}, + {file = "safetensors-0.4.5-cp37-none-win32.whl", hash = "sha256:6d3de65718b86c3eeaa8b73a9c3d123f9307a96bbd7be9698e21e76a56443af5"}, + {file = "safetensors-0.4.5-cp37-none-win_amd64.whl", hash = "sha256:5a2d68a523a4cefd791156a4174189a4114cf0bf9c50ceb89f261600f3b2b81a"}, + {file = "safetensors-0.4.5-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:e7a97058f96340850da0601a3309f3d29d6191b0702b2da201e54c6e3e44ccf0"}, + {file = "safetensors-0.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:63bfd425e25f5c733f572e2246e08a1c38bd6f2e027d3f7c87e2e43f228d1345"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3664ac565d0e809b0b929dae7ccd74e4d3273cd0c6d1220c6430035befb678e"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:313514b0b9b73ff4ddfb4edd71860696dbe3c1c9dc4d5cc13dbd74da283d2cbf"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31fa33ee326f750a2f2134a6174773c281d9a266ccd000bd4686d8021f1f3dac"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09566792588d77b68abe53754c9f1308fadd35c9f87be939e22c623eaacbed6b"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309aaec9b66cbf07ad3a2e5cb8a03205663324fea024ba391594423d0f00d9fe"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53946c5813b8f9e26103c5efff4a931cc45d874f45229edd68557ffb35ffb9f8"}, + {file = "safetensors-0.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:868f9df9e99ad1e7f38c52194063a982bc88fedc7d05096f4f8160403aaf4bd6"}, + {file = "safetensors-0.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9cc9449bd0b0bc538bd5e268221f0c5590bc5c14c1934a6ae359d44410dc68c4"}, + {file = "safetensors-0.4.5-cp38-none-win32.whl", hash = "sha256:83c4f13a9e687335c3928f615cd63a37e3f8ef072a3f2a0599fa09f863fb06a2"}, + {file = "safetensors-0.4.5-cp38-none-win_amd64.whl", hash = "sha256:b98d40a2ffa560653f6274e15b27b3544e8e3713a44627ce268f419f35c49478"}, + {file = "safetensors-0.4.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cf727bb1281d66699bef5683b04d98c894a2803442c490a8d45cd365abfbdeb2"}, + {file = "safetensors-0.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96f1d038c827cdc552d97e71f522e1049fef0542be575421f7684756a748e457"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:139fbee92570ecea774e6344fee908907db79646d00b12c535f66bc78bd5ea2c"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c36302c1c69eebb383775a89645a32b9d266878fab619819ce660309d6176c9b"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d641f5b8149ea98deb5ffcf604d764aad1de38a8285f86771ce1abf8e74c4891"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b4db6a61d968de73722b858038c616a1bebd4a86abe2688e46ca0cc2d17558f2"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b75a616e02f21b6f1d5785b20cecbab5e2bd3f6358a90e8925b813d557666ec1"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:788ee7d04cc0e0e7f944c52ff05f52a4415b312f5efd2ee66389fb7685ee030c"}, + {file = "safetensors-0.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:87bc42bd04fd9ca31396d3ca0433db0be1411b6b53ac5a32b7845a85d01ffc2e"}, + {file = "safetensors-0.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4037676c86365a721a8c9510323a51861d703b399b78a6b4486a54a65a975fca"}, + {file = "safetensors-0.4.5-cp39-none-win32.whl", hash = "sha256:1500418454529d0ed5c1564bda376c4ddff43f30fce9517d9bee7bcce5a8ef50"}, + {file = "safetensors-0.4.5-cp39-none-win_amd64.whl", hash = "sha256:9d1a94b9d793ed8fe35ab6d5cea28d540a46559bafc6aae98f30ee0867000cab"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdadf66b5a22ceb645d5435a0be7a0292ce59648ca1d46b352f13cff3ea80410"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d42ffd4c2259f31832cb17ff866c111684c87bd930892a1ba53fed28370c918c"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd8a1f6d2063a92cd04145c7fd9e31a1c7d85fbec20113a14b487563fdbc0597"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:951d2fcf1817f4fb0ef0b48f6696688a4e852a95922a042b3f96aaa67eedc920"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ac85d9a8c1af0e3132371d9f2d134695a06a96993c2e2f0bbe25debb9e3f67a"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e3cec4a29eb7fe8da0b1c7988bc3828183080439dd559f720414450de076fcab"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:21742b391b859e67b26c0b2ac37f52c9c0944a879a25ad2f9f9f3cd61e7fda8f"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7db3006a4915151ce1913652e907cdede299b974641a83fbc092102ac41b644"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f68bf99ea970960a237f416ea394e266e0361895753df06e3e06e6ea7907d98b"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8158938cf3324172df024da511839d373c40fbfaa83e9abf467174b2910d7b4c"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:540ce6c4bf6b58cb0fd93fa5f143bc0ee341c93bb4f9287ccd92cf898cc1b0dd"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bfeaa1a699c6b9ed514bd15e6a91e74738b71125a9292159e3d6b7f0a53d2cde"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:01c8f00da537af711979e1b42a69a8ec9e1d7112f208e0e9b8a35d2c381085ef"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a0dd565f83b30f2ca79b5d35748d0d99dd4b3454f80e03dfb41f0038e3bdf180"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:023b6e5facda76989f4cba95a861b7e656b87e225f61811065d5c501f78cdb3f"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9633b663393d5796f0b60249549371e392b75a0b955c07e9c6f8708a87fc841f"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78dd8adfb48716233c45f676d6e48534d34b4bceb50162c13d1f0bdf6f78590a"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e8deb16c4321d61ae72533b8451ec4a9af8656d1c61ff81aa49f966406e4b68"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:52452fa5999dc50c4decaf0c53aa28371f7f1e0fe5c2dd9129059fbe1e1599c7"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d5f23198821e227cfc52d50fa989813513db381255c6d100927b012f0cfec63d"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f4beb84b6073b1247a773141a6331117e35d07134b3bb0383003f39971d414bb"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:68814d599d25ed2fdd045ed54d370d1d03cf35e02dce56de44c651f828fb9b7b"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0b6453c54c57c1781292c46593f8a37254b8b99004c68d6c3ce229688931a22"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adaa9c6dead67e2dd90d634f89131e43162012479d86e25618e821a03d1eb1dc"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73e7d408e9012cd17511b382b43547850969c7979efc2bc353f317abaf23c84c"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:775409ce0fcc58b10773fdb4221ed1eb007de10fe7adbdf8f5e8a56096b6f0bc"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:834001bed193e4440c4a3950a31059523ee5090605c907c66808664c932b549c"}, + {file = "safetensors-0.4.5.tar.gz", hash = "sha256:d73de19682deabb02524b3d5d1f8b3aaba94c72f1bbfc7911b9b9d5d391c0310"}, +] + +[package.extras] +all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"] +dev = ["safetensors[all]"] +jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"] +mlx = ["mlx (>=0.0.9)"] +numpy = ["numpy (>=1.21.6)"] +paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"] +pinned-tf = ["safetensors[numpy]", "tensorflow (==2.11.0)"] +quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] +tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] +testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] +torch = ["safetensors[numpy]", "torch (>=1.10)"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.41.3" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, + {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "tokenizers" +version = "0.21.0" +description = "" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, + {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff"}, + {file = "tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a"}, + {file = "tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c"}, + {file = "tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + +[[package]] +name = "tqdm" +version = "4.67.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "transformers" +version = "4.47.1" +description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +optional = false +python-versions = ">=3.9.0" +groups = ["main"] +files = [ + {file = "transformers-4.47.1-py3-none-any.whl", hash = "sha256:d2f5d19bb6283cd66c893ec7e6d931d6370bbf1cc93633326ff1f41a40046c9c"}, + {file = "transformers-4.47.1.tar.gz", hash = "sha256:6c29c05a5f595e278481166539202bf8641281536df1c42357ee58a45d0a564a"}, +] + +[package.dependencies] +filelock = "*" +huggingface-hub = ">=0.24.0,<1.0" +numpy = ">=1.17" +packaging = ">=20.0" +pyyaml = ">=5.1" +regex = "!=2019.12.17" +requests = "*" +safetensors = ">=0.4.1" +tokenizers = ">=0.21,<0.22" +tqdm = ">=4.27" + +[package.extras] +accelerate = ["accelerate (>=0.26.0)"] +agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] +all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch", "torchaudio", "torchvision"] +audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +benchmark = ["optimum-benchmark (>=0.3.0)"] +codecarbon = ["codecarbon (==1.2.0)"] +deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.21,<0.22)", "urllib3 (<2.0.0)"] +dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "libcst", "librosa", "nltk (<=3.8.1)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] +flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +ftfy = ["ftfy"] +integrations = ["optuna", "ray[tune] (>=2.7.0)", "sigopt"] +ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +modelcreation = ["cookiecutter (==1.7.3)"] +natten = ["natten (>=0.14.6,<0.15.0)"] +onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] +onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] +optuna = ["optuna"] +quality = ["GitPython (<3.1.19)", "datasets (!=2.5.0)", "isort (>=5.5.4)", "libcst", "rich", "ruff (==0.5.1)", "urllib3 (<2.0.0)"] +ray = ["ray[tune] (>=2.7.0)"] +retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] +ruff = ["ruff (==0.5.1)"] +sagemaker = ["sagemaker (>=2.31.0)"] +sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] +serving = ["fastapi", "pydantic", "starlette", "uvicorn"] +sigopt = ["sigopt"] +sklearn = ["scikit-learn"] +speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] +tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] +tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +tiktoken = ["blobfile", "tiktoken"] +timm = ["timm (<=1.0.11)"] +tokenizers = ["tokenizers (>=0.21,<0.22)"] +torch = ["accelerate (>=0.26.0)", "torch"] +torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] +torchhub = ["filelock", "huggingface-hub (>=0.24.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.21,<0.22)", "torch", "tqdm (>=4.27)"] +video = ["av (==9.2.0)"] +vision = ["Pillow (>=10.0.1,<=15.0)"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.34.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, + {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.6.3", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.21.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\"" +files = [ + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"}, + {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"}, +] + +[package.extras] +dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "watchfiles" +version = "1.0.3" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "watchfiles-1.0.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42"}, + {file = "watchfiles-1.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34e87c7b3464d02af87f1059fedda5484e43b153ef519e4085fe1a03dd94801e"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d9dd2b89a16cf7ab9c1170b5863e68de6bf83db51544875b25a5f05a7269e678"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b4691234d31686dca133c920f94e478b548a8e7c750f28dbbc2e4333e0d3da9"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90b0fe1fcea9bd6e3084b44875e179b4adcc4057a3b81402658d0eb58c98edf8"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b90651b4cf9e158d01faa0833b073e2e37719264bcee3eac49fc3c74e7d304b"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2e9fe695ff151b42ab06501820f40d01310fbd58ba24da8923ace79cf6d702d"}, + {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62691f1c0894b001c7cde1195c03b7801aaa794a837bd6eef24da87d1542838d"}, + {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:275c1b0e942d335fccb6014d79267d1b9fa45b5ac0639c297f1e856f2f532552"}, + {file = "watchfiles-1.0.3-cp310-cp310-win32.whl", hash = "sha256:06ce08549e49ba69ccc36fc5659a3d0ff4e3a07d542b895b8a9013fcab46c2dc"}, + {file = "watchfiles-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f280b02827adc9d87f764972fbeb701cf5611f80b619c20568e1982a277d6146"}, + {file = "watchfiles-1.0.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f"}, + {file = "watchfiles-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77"}, + {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469"}, + {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780"}, + {file = "watchfiles-1.0.3-cp311-cp311-win32.whl", hash = "sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181"}, + {file = "watchfiles-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c"}, + {file = "watchfiles-1.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6"}, + {file = "watchfiles-1.0.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93436ed550e429da007fbafb723e0769f25bae178fbb287a94cb4ccdf42d3af3"}, + {file = "watchfiles-1.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c18f3502ad0737813c7dad70e3e1cc966cc147fbaeef47a09463bbffe70b0a00"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5bc3ca468bb58a2ef50441f953e1f77b9a61bd1b8c347c8223403dc9b4ac9a"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d1ec043f02ca04bf21b1b32cab155ce90c651aaf5540db8eb8ad7f7e645cba8"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f58d3bfafecf3d81c15d99fc0ecf4319e80ac712c77cf0ce2661c8cf8bf84066"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1df924ba82ae9e77340101c28d56cbaff2c991bd6fe8444a545d24075abb0a87"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632a52dcaee44792d0965c17bdfe5dc0edad5b86d6a29e53d6ad4bf92dc0ff49"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bf4b459d94a0387617a1b499f314aa04d8a64b7a0747d15d425b8c8b151da0"}, + {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca94c85911601b097d53caeeec30201736ad69a93f30d15672b967558df02885"}, + {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65ab1fb635476f6170b07e8e21db0424de94877e4b76b7feabfe11f9a5fc12b5"}, + {file = "watchfiles-1.0.3-cp312-cp312-win32.whl", hash = "sha256:49bc1bc26abf4f32e132652f4b3bfeec77d8f8f62f57652703ef127e85a3e38d"}, + {file = "watchfiles-1.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:48681c86f2cb08348631fed788a116c89c787fdf1e6381c5febafd782f6c3b44"}, + {file = "watchfiles-1.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:9e080cf917b35b20c889225a13f290f2716748362f6071b859b60b8847a6aa43"}, + {file = "watchfiles-1.0.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e153a690b7255c5ced17895394b4f109d5dcc2a4f35cb809374da50f0e5c456a"}, + {file = "watchfiles-1.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac1be85fe43b4bf9a251978ce5c3bb30e1ada9784290441f5423a28633a958a7"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec98e31e1844eac860e70d9247db9d75440fc8f5f679c37d01914568d18721"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0179252846be03fa97d4d5f8233d1c620ef004855f0717712ae1c558f1974a16"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995c374e86fa82126c03c5b4630c4e312327ecfe27761accb25b5e1d7ab50ec8"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b9cb35b7f290db1c31fb2fdf8fc6d3730cfa4bca4b49761083307f441cac5a"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f8dc09ae69af50bead60783180f656ad96bd33ffbf6e7a6fce900f6d53b08f1"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:489b80812f52a8d8c7b0d10f0d956db0efed25df2821c7a934f6143f76938bd6"}, + {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:228e2247de583475d4cebf6b9af5dc9918abb99d1ef5ee737155bb39fb33f3c0"}, + {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1550be1a5cb3be08a3fb84636eaafa9b7119b70c71b0bed48726fd1d5aa9b868"}, + {file = "watchfiles-1.0.3-cp313-cp313-win32.whl", hash = "sha256:16db2d7e12f94818cbf16d4c8938e4d8aaecee23826344addfaaa671a1527b07"}, + {file = "watchfiles-1.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:160eff7d1267d7b025e983ca8460e8cc67b328284967cbe29c05f3c3163711a3"}, + {file = "watchfiles-1.0.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c05b021f7b5aa333124f2a64d56e4cb9963b6efdf44e8d819152237bbd93ba15"}, + {file = "watchfiles-1.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:310505ad305e30cb6c5f55945858cdbe0eb297fc57378f29bacceb534ac34199"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddff3f8b9fa24a60527c137c852d0d9a7da2a02cf2151650029fdc97c852c974"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46e86ed457c3486080a72bc837300dd200e18d08183f12b6ca63475ab64ed651"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f79fe7993e230a12172ce7d7c7db061f046f672f2b946431c81aff8f60b2758b"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea2b51c5f38bad812da2ec0cd7eec09d25f521a8b6b6843cbccedd9a1d8a5c15"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fe4e740ea94978b2b2ab308cbf9270a246bcbb44401f77cc8740348cbaeac3d"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9af037d3df7188ae21dc1c7624501f2f90d81be6550904e07869d8d0e6766655"}, + {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52bb50a4c4ca2a689fdba84ba8ecc6a4e6210f03b6af93181bb61c4ec3abaf86"}, + {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c14a07bdb475eb696f85c715dbd0f037918ccbb5248290448488a0b4ef201aad"}, + {file = "watchfiles-1.0.3-cp39-cp39-win32.whl", hash = "sha256:be37f9b1f8934cd9e7eccfcb5612af9fb728fecbe16248b082b709a9d1b348bf"}, + {file = "watchfiles-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ef9ec8068cf23458dbf36a08e0c16f0a2df04b42a8827619646637be1769300a"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:84fac88278f42d61c519a6c75fb5296fd56710b05bbdcc74bdf85db409a03780"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c68be72b1666d93b266714f2d4092d78dc53bd11cf91ed5a3c16527587a52e29"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889a37e2acf43c377b5124166bece139b4c731b61492ab22e64d371cce0e6e80"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca05cacf2e5c4a97d02a2878a24020daca21dbb8823b023b978210a75c79098"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8af4b582d5fc1b8465d1d2483e5e7b880cc1a4e99f6ff65c23d64d070867ac58"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:127de3883bdb29dbd3b21f63126bb8fa6e773b74eaef46521025a9ce390e1073"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:713f67132346bdcb4c12df185c30cf04bdf4bf6ea3acbc3ace0912cab6b7cb8c"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abd85de513eb83f5ec153a802348e7a5baa4588b818043848247e3e8986094e8"}, + {file = "watchfiles-1.0.3.tar.gz", hash = "sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "14.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +description = "Python binding for xxHash" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"}, + {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"}, + {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"}, + {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"}, + {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"}, + {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"}, + {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"}, + {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"}, + {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"}, + {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"}, + {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"}, + {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"}, + {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"}, + {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"}, + {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"}, + {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"}, + {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"}, + {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"}, + {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"}, + {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"}, + {file = "xxhash-3.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6e5f70f6dca1d3b09bccb7daf4e087075ff776e3da9ac870f86ca316736bb4aa"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e76e83efc7b443052dd1e585a76201e40b3411fe3da7af4fe434ec51b2f163b"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33eac61d0796ca0591f94548dcfe37bb193671e0c9bcf065789b5792f2eda644"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ec70a89be933ea49222fafc3999987d7899fc676f688dd12252509434636622"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86b8e7f703ec6ff4f351cfdb9f428955859537125904aa8c963604f2e9d3e7"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0adfbd36003d9f86c8c97110039f7539b379f28656a04097e7434d3eaf9aa131"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:63107013578c8a730419adc05608756c3fa640bdc6abe806c3123a49fb829f43"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:683b94dbd1ca67557850b86423318a2e323511648f9f3f7b1840408a02b9a48c"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5d2a01dcce81789cf4b12d478b5464632204f4c834dc2d064902ee27d2d1f0ee"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:a9d360a792cbcce2fe7b66b8d51274ec297c53cbc423401480e53b26161a290d"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f0b48edbebea1b7421a9c687c304f7b44d0677c46498a046079d445454504737"}, + {file = "xxhash-3.5.0-cp37-cp37m-win32.whl", hash = "sha256:7ccb800c9418e438b44b060a32adeb8393764da7441eb52aa2aa195448935306"}, + {file = "xxhash-3.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c3bc7bf8cb8806f8d1c9bf149c18708cb1c406520097d6b0a73977460ea03602"}, + {file = "xxhash-3.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74752ecaa544657d88b1d1c94ae68031e364a4d47005a90288f3bab3da3c970f"}, + {file = "xxhash-3.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dee1316133c9b463aa81aca676bc506d3f80d8f65aeb0bba2b78d0b30c51d7bd"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:602d339548d35a8579c6b013339fb34aee2df9b4e105f985443d2860e4d7ffaa"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:695735deeddfb35da1677dbc16a083445360e37ff46d8ac5c6fcd64917ff9ade"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1030a39ba01b0c519b1a82f80e8802630d16ab95dc3f2b2386a0b5c8ed5cbb10"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5bc08f33c4966f4eb6590d6ff3ceae76151ad744576b5fc6c4ba8edd459fdec"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160e0c19ee500482ddfb5d5570a0415f565d8ae2b3fd69c5dcfce8a58107b1c3"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f1abffa122452481a61c3551ab3c89d72238e279e517705b8b03847b1d93d738"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5e9db7ef3ecbfc0b4733579cea45713a76852b002cf605420b12ef3ef1ec148"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:23241ff6423378a731d84864bf923a41649dc67b144debd1077f02e6249a0d54"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:82b833d5563fefd6fceafb1aed2f3f3ebe19f84760fdd289f8b926731c2e6e91"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a80ad0ffd78bef9509eee27b4a29e56f5414b87fb01a888353e3d5bda7038bd"}, + {file = "xxhash-3.5.0-cp38-cp38-win32.whl", hash = "sha256:50ac2184ffb1b999e11e27c7e3e70cc1139047e7ebc1aa95ed12f4269abe98d4"}, + {file = "xxhash-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:392f52ebbb932db566973693de48f15ce787cabd15cf6334e855ed22ea0be5b3"}, + {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"}, + {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"}, + {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"}, + {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"}, + {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b4154c00eb22e4d543f472cfca430e7962a0f1d0f3778334f2e08a7ba59363c"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d30bbc1644f726b825b3278764240f449d75f1a8bdda892e641d4a688b1494ae"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0b72f2423e2aa53077e54a61c28e181d23effeaafd73fcb9c494e60930c8e"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13de2b76c1835399b2e419a296d5b38dc4855385d9e96916299170085ef72f57"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0691bfcc4f9c656bcb96cc5db94b4d75980b9d5589f2e59de790091028580837"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:297595fe6138d4da2c8ce9e72a04d73e58725bb60f3a19048bc96ab2ff31c692"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc1276d369452040cbb943300dc8abeedab14245ea44056a2943183822513a18"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2061188a1ba352fc699c82bff722f4baacb4b4b8b2f0c745d2001e56d0dfb514"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c384c434021e4f62b8d9ba0bc9467e14d394893077e2c66d826243025e1f81"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e6a4dd644d72ab316b580a1c120b375890e4c52ec392d4aef3c63361ec4d77d1"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"}, + {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"}, +] + +[[package]] +name = "yarl" +version = "1.18.3" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.0" + +[metadata] +lock-version = "2.1" +python-versions = "^3.12" +content-hash = "fe454ce23e263fc9f41c98ec667b1770b0587abf6c488ff9ab67ab1ad048b960" diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..a5f0adaaa53dd00c497a17c54a5dd96fbb1bb288 --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,32 @@ +[tool.poetry] +name = "llm-leaderboard-backend" +version = "0.1.0" +description = "Backend for the Open LLM Leaderboard" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.12" +fastapi = "^0.115.6" +uvicorn = {extras = ["standard"], version = "^0.34.0"} +numpy = "^2.2.0" +pandas = "^2.2.3" +datasets = "^3.2.0" +pyarrow = "^18.1.0" +python-multipart = "^0.0.20" +huggingface-hub = "^0.27.0" +transformers = "^4.47.0" +safetensors = "^0.4.5" +aiofiles = "^24.1.0" +fastapi-cache2 = "^0.2.1" +python-dotenv = "^1.0.1" +pydantic = "^2.10.4" + +[tool.poetry.group.dev.dependencies] +pytest = "^8.3.4" +black = "^24.10.0" +isort = "^5.13.2" +flake8 = "^6.1.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/backend/utils/analyze_prod_datasets.py b/backend/utils/analyze_prod_datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..f05b1c257045bdbc6ef4d782626cae37aa76c14a --- /dev/null +++ b/backend/utils/analyze_prod_datasets.py @@ -0,0 +1,170 @@ +import os +import json +import logging +from datetime import datetime +from pathlib import Path +from typing import Dict, Any, List +from huggingface_hub import HfApi +from dotenv import load_dotenv +from app.config.hf_config import HF_ORGANIZATION + +# Get the backend directory path +BACKEND_DIR = Path(__file__).parent.parent +ROOT_DIR = BACKEND_DIR.parent + +# Load environment variables from .env file in root directory +load_dotenv(ROOT_DIR / ".env") + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(message)s' +) +logger = logging.getLogger(__name__) + +# Initialize Hugging Face API +HF_TOKEN = os.getenv("HF_TOKEN") +if not HF_TOKEN: + raise ValueError("HF_TOKEN not found in environment variables") +api = HfApi(token=HF_TOKEN) + +def analyze_dataset(repo_id: str) -> Dict[str, Any]: + """Analyze a dataset and return statistics""" + try: + # Get dataset info + dataset_info = api.dataset_info(repo_id=repo_id) + + # Get file list + files = api.list_repo_files(repo_id, repo_type="dataset") + + # Get last commit info + commits = api.list_repo_commits(repo_id, repo_type="dataset") + last_commit = next(commits, None) + + # Count lines in jsonl files + total_entries = 0 + for file in files: + if file.endswith('.jsonl'): + try: + # Download file content + content = api.hf_hub_download( + repo_id=repo_id, + filename=file, + repo_type="dataset" + ) + + # Count lines + with open(content, 'r') as f: + for _ in f: + total_entries += 1 + + except Exception as e: + logger.error(f"Error processing file {file}: {str(e)}") + continue + + # Special handling for requests dataset + if repo_id == f"{HF_ORGANIZATION}/requests": + pending_count = 0 + completed_count = 0 + + try: + content = api.hf_hub_download( + repo_id=repo_id, + filename="eval_requests.jsonl", + repo_type="dataset" + ) + + with open(content, 'r') as f: + for line in f: + try: + entry = json.loads(line) + if entry.get("status") == "pending": + pending_count += 1 + elif entry.get("status") == "completed": + completed_count += 1 + except json.JSONDecodeError: + continue + + except Exception as e: + logger.error(f"Error analyzing requests: {str(e)}") + + # Build response + response = { + "id": repo_id, + "last_modified": last_commit.created_at if last_commit else None, + "total_entries": total_entries, + "file_count": len(files), + "size_bytes": dataset_info.size_in_bytes, + "downloads": dataset_info.downloads, + } + + # Add request-specific info if applicable + if repo_id == f"{HF_ORGANIZATION}/requests": + response.update({ + "pending_requests": pending_count, + "completed_requests": completed_count + }) + + return response + + except Exception as e: + logger.error(f"Error analyzing dataset {repo_id}: {str(e)}") + return { + "id": repo_id, + "error": str(e) + } + +def main(): + """Main function to analyze all datasets""" + try: + # List of datasets to analyze + datasets = [ + { + "id": f"{HF_ORGANIZATION}/contents", + "description": "Aggregated results" + }, + { + "id": f"{HF_ORGANIZATION}/requests", + "description": "Evaluation requests" + }, + { + "id": f"{HF_ORGANIZATION}/votes", + "description": "User votes" + }, + { + "id": f"{HF_ORGANIZATION}/maintainers-highlight", + "description": "Highlighted models" + } + ] + + # Analyze each dataset + results = [] + for dataset in datasets: + logger.info(f"\nAnalyzing {dataset['description']} ({dataset['id']})...") + result = analyze_dataset(dataset['id']) + results.append(result) + + if 'error' in result: + logger.error(f"❌ Error: {result['error']}") + else: + logger.info(f"✓ {result['total_entries']} entries") + logger.info(f"✓ {result['file_count']} files") + logger.info(f"✓ {result['size_bytes'] / 1024:.1f} KB") + logger.info(f"✓ {result['downloads']} downloads") + + if 'pending_requests' in result: + logger.info(f"✓ {result['pending_requests']} pending requests") + logger.info(f"✓ {result['completed_requests']} completed requests") + + if result['last_modified']: + last_modified = datetime.fromisoformat(result['last_modified'].replace('Z', '+00:00')) + logger.info(f"✓ Last modified: {last_modified.strftime('%Y-%m-%d %H:%M:%S')}") + + return results + + except Exception as e: + logger.error(f"Global error: {str(e)}") + return [] + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/backend/utils/analyze_prod_models.py b/backend/utils/analyze_prod_models.py new file mode 100644 index 0000000000000000000000000000000000000000..4da1c7ee9832608f7ab79ed08c751deb11b38445 --- /dev/null +++ b/backend/utils/analyze_prod_models.py @@ -0,0 +1,106 @@ +import os +import json +import logging +from datetime import datetime +from pathlib import Path +from huggingface_hub import HfApi +from dotenv import load_dotenv +from app.config.hf_config import HF_ORGANIZATION + +# Get the backend directory path +BACKEND_DIR = Path(__file__).parent.parent +ROOT_DIR = BACKEND_DIR.parent + +# Load environment variables from .env file in root directory +load_dotenv(ROOT_DIR / ".env") + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(message)s' +) +logger = logging.getLogger(__name__) + +# Initialize Hugging Face API +HF_TOKEN = os.getenv("HF_TOKEN") +if not HF_TOKEN: + raise ValueError("HF_TOKEN not found in environment variables") +api = HfApi(token=HF_TOKEN) + +def count_evaluated_models(): + """Count the number of evaluated models""" + try: + # Get dataset info + dataset_info = api.dataset_info(repo_id=f"{HF_ORGANIZATION}/greek-contents", repo_type="dataset") + + # Get file list + files = api.list_repo_files(f"{HF_ORGANIZATION}/greek-contents", repo_type="dataset") + + # Get last commit info + commits = api.list_repo_commits(f"{HF_ORGANIZATION}/greek-contents", repo_type="dataset") + last_commit = next(commits, None) + + # Count lines in jsonl files + total_entries = 0 + for file in files: + if file.endswith('.jsonl'): + try: + # Download file content + content = api.hf_hub_download( + repo_id=f"{HF_ORGANIZATION}/greek-contents", + filename=file, + repo_type="dataset" + ) + + # Count lines + with open(content, 'r') as f: + for _ in f: + total_entries += 1 + + except Exception as e: + logger.error(f"Error processing file {file}: {str(e)}") + continue + + # Build response + response = { + "total_models": total_entries, + "last_modified": last_commit.created_at if last_commit else None, + "file_count": len(files), + "size_bytes": dataset_info.size_in_bytes, + "downloads": dataset_info.downloads + } + + return response + + except Exception as e: + logger.error(f"Error counting evaluated models: {str(e)}") + return { + "error": str(e) + } + +def main(): + """Main function to count evaluated models""" + try: + logger.info("\nAnalyzing evaluated models...") + result = count_evaluated_models() + + if 'error' in result: + logger.error(f"❌ Error: {result['error']}") + else: + logger.info(f"✓ {result['total_models']} models evaluated") + logger.info(f"✓ {result['file_count']} files") + logger.info(f"✓ {result['size_bytes'] / 1024:.1f} KB") + logger.info(f"✓ {result['downloads']} downloads") + + if result['last_modified']: + last_modified = datetime.fromisoformat(result['last_modified'].replace('Z', '+00:00')) + logger.info(f"✓ Last modified: {last_modified.strftime('%Y-%m-%d %H:%M:%S')}") + + return result + + except Exception as e: + logger.error(f"Global error: {str(e)}") + return {"error": str(e)} + +if __name__ == "__main__": + main() diff --git a/backend/utils/fix_wrong_model_size.py b/backend/utils/fix_wrong_model_size.py new file mode 100644 index 0000000000000000000000000000000000000000..16ef1ab4ad553ef6c1e95707e5aeffab31d2f66e --- /dev/null +++ b/backend/utils/fix_wrong_model_size.py @@ -0,0 +1,110 @@ +import os +import json +import pytz +import logging +import asyncio +from datetime import datetime +from pathlib import Path +import huggingface_hub +from huggingface_hub.errors import RepositoryNotFoundError, RevisionNotFoundError +from dotenv import load_dotenv +from git import Repo +from datetime import datetime +from tqdm.auto import tqdm +from tqdm.contrib.logging import logging_redirect_tqdm + +from app.config.hf_config import HF_TOKEN, QUEUE_REPO, API, EVAL_REQUESTS_PATH + +from app.utils.model_validation import ModelValidator + +huggingface_hub.logging.set_verbosity_error() +huggingface_hub.utils.disable_progress_bars() + +logging.basicConfig( + level=logging.ERROR, + format='%(message)s' +) +logger = logging.getLogger(__name__) +load_dotenv() + +validator = ModelValidator() + +def get_changed_files(repo_path, start_date, end_date): + repo = Repo(repo_path) + start = datetime.strptime(start_date, '%Y-%m-%d') + end = datetime.strptime(end_date, '%Y-%m-%d') + + changed_files = set() + pbar = tqdm(repo.iter_commits(), desc=f"Reading commits from {end_date} to {start_date}") + for commit in pbar: + commit_date = datetime.fromtimestamp(commit.committed_date) + pbar.set_postfix_str(f"Commit date: {commit_date}") + if start <= commit_date <= end: + changed_files.update(item.a_path for item in commit.diff(commit.parents[0])) + + if commit_date < start: + break + + return changed_files + + +def read_json(repo_path, file): + with open(f"{repo_path}/{file}") as file: + return json.load(file) + + +def write_json(repo_path, file, content): + with open(f"{repo_path}/{file}", "w") as file: + json.dump(content, file, indent=2) + + +def main(): + requests_path = "/Users/lozowski/Developer/requests" + start_date = "2024-12-09" + end_date = "2025-01-07" + + changed_files = get_changed_files(requests_path, start_date, end_date) + + for file in tqdm(changed_files): + try: + request_data = read_json(requests_path, file) + except FileNotFoundError as e: + tqdm.write(f"File {file} not found") + continue + + try: + model_info = API.model_info( + repo_id=request_data["model"], + revision=request_data["revision"], + token=HF_TOKEN + ) + except (RepositoryNotFoundError, RevisionNotFoundError) as e: + tqdm.write(f"Model info for {request_data["model"]} not found") + continue + + with logging_redirect_tqdm(): + new_model_size, error = asyncio.run(validator.get_model_size( + model_info=model_info, + precision=request_data["precision"], + base_model=request_data["base_model"], + revision=request_data["revision"] + )) + + if error: + tqdm.write(f"Error getting model size info for {request_data["model"]}, {error}") + continue + + old_model_size = request_data["params"] + if old_model_size != new_model_size: + if new_model_size > 100: + tqdm.write(f"Model: {request_data["model"]}, size is more 100B: {new_model_size}") + + tqdm.write(f"Model: {request_data["model"]}, old size: {request_data["params"]} new size: {new_model_size}") + tqdm.write(f"Updating request file {file}") + + request_data["params"] = new_model_size + write_json(requests_path, file, content=request_data) + + +if __name__ == "__main__": + main() diff --git a/backend/utils/last_activity.py b/backend/utils/last_activity.py new file mode 100644 index 0000000000000000000000000000000000000000..2818a66f0489a99ab368c830a1060267c3f3572a --- /dev/null +++ b/backend/utils/last_activity.py @@ -0,0 +1,164 @@ +import os +import json +import logging +from datetime import datetime +from pathlib import Path +from typing import Dict, Any, List, Tuple +from huggingface_hub import HfApi +from dotenv import load_dotenv + +# Get the backend directory path +BACKEND_DIR = Path(__file__).parent.parent +ROOT_DIR = BACKEND_DIR.parent + +# Load environment variables from .env file in root directory +load_dotenv(ROOT_DIR / ".env") + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(message)s' +) +logger = logging.getLogger(__name__) + +# Initialize Hugging Face API +HF_TOKEN = os.getenv("HF_TOKEN") +if not HF_TOKEN: + raise ValueError("HF_TOKEN not found in environment variables") +api = HfApi(token=HF_TOKEN) + +# Default organization +HF_ORGANIZATION = os.getenv('HF_ORGANIZATION', 'TheFinAI') + +def get_last_votes(limit: int = 5) -> List[Dict]: + """Get the last votes from the votes dataset""" + try: + logger.info("\nFetching last votes...") + + # Download and read votes file + logger.info("Downloading votes file...") + votes_file = api.hf_hub_download( + repo_id=f"{HF_ORGANIZATION}/votes", + filename="votes_data.jsonl", + repo_type="dataset" + ) + + logger.info("Reading votes file...") + votes = [] + with open(votes_file, 'r') as f: + for line in f: + try: + vote = json.loads(line) + votes.append(vote) + except json.JSONDecodeError: + continue + + # Sort by timestamp and get last n votes + logger.info("Sorting votes...") + votes.sort(key=lambda x: x.get('timestamp', ''), reverse=True) + last_votes = votes[:limit] + + logger.info(f"✓ Found {len(last_votes)} recent votes") + return last_votes + + except Exception as e: + logger.error(f"Error reading votes: {str(e)}") + return [] + +def get_last_models(limit: int = 5) -> List[Dict]: + """Get the last models from the requests dataset using commit history""" + try: + logger.info("\nFetching last model submissions...") + + # Get commit history + logger.info("Getting commit history...") + commits = list(api.list_repo_commits( + repo_id=f"{HF_ORGANIZATION}/requests", + repo_type="dataset" + )) + logger.info(f"Found {len(commits)} commits") + + # Track processed files to avoid duplicates + processed_files = set() + models = [] + + # Process commits until we have enough models + for i, commit in enumerate(commits): + logger.info(f"Processing commit {i+1}/{len(commits)} ({commit.created_at})") + + # Look at added/modified files in this commit + files_to_process = [f for f in (commit.added + commit.modified) if f.endswith('.json')] + if files_to_process: + logger.info(f"Found {len(files_to_process)} JSON files in commit") + + for file in files_to_process: + if file in processed_files: + continue + + processed_files.add(file) + logger.info(f"Downloading {file}...") + + try: + # Download and read the file + content = api.hf_hub_download( + repo_id=f"{HF_ORGANIZATION}/requests", + filename=file, + repo_type="dataset" + ) + + with open(content, 'r') as f: + model_data = json.load(f) + models.append(model_data) + logger.info(f"✓ Added model {model_data.get('model', 'Unknown')}") + + if len(models) >= limit: + logger.info("Reached desired number of models") + break + + except Exception as e: + logger.error(f"Error reading file {file}: {str(e)}") + continue + + if len(models) >= limit: + break + + logger.info(f"✓ Found {len(models)} recent model submissions") + return models + + except Exception as e: + logger.error(f"Error reading models: {str(e)}") + return [] + +def main(): + """Display last activities from the leaderboard""" + try: + # Get last votes + logger.info("\n=== Last Votes ===") + last_votes = get_last_votes() + if last_votes: + for vote in last_votes: + logger.info(f"\nModel: {vote.get('model')}") + logger.info(f"User: {vote.get('username')}") + logger.info(f"Timestamp: {vote.get('timestamp')}") + else: + logger.info("No votes found") + + # Get last model submissions + logger.info("\n=== Last Model Submissions ===") + last_models = get_last_models() + if last_models: + for model in last_models: + logger.info(f"\nModel: {model.get('model')}") + logger.info(f"Submitter: {model.get('sender', 'Unknown')}") + logger.info(f"Status: {model.get('status', 'Unknown')}") + logger.info(f"Submission Time: {model.get('submitted_time', 'Unknown')}") + logger.info(f"Precision: {model.get('precision', 'Unknown')}") + logger.info(f"Weight Type: {model.get('weight_type', 'Unknown')}") + else: + logger.info("No models found") + + except Exception as e: + logger.error(f"Global error: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/backend/utils/sync_datasets_locally.py b/backend/utils/sync_datasets_locally.py new file mode 100644 index 0000000000000000000000000000000000000000..c4edba23176c60fa4f1a856a2bd1510e773331ab --- /dev/null +++ b/backend/utils/sync_datasets_locally.py @@ -0,0 +1,130 @@ +import os +import shutil +import tempfile +import logging +from pathlib import Path +from huggingface_hub import HfApi, snapshot_download, upload_folder, create_repo +from dotenv import load_dotenv + +# Configure source and destination usernames +SOURCE_USERNAME = "TheFinAI" +DESTINATION_USERNAME = "tfrere" + +# Get the backend directory path +BACKEND_DIR = Path(__file__).parent.parent +ROOT_DIR = BACKEND_DIR.parent + +# Load environment variables from .env file in root directory +load_dotenv(ROOT_DIR / ".env") + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(message)s' +) +logger = logging.getLogger(__name__) + +# List of dataset names to sync +DATASET_NAMES = [ + "votes", + "results", + "requests", + "greek-contents", + "maintainers-highlight", +] + +# Build list of datasets with their source and destination paths +DATASETS = [ + (name, f"{SOURCE_USERNAME}/{name}", f"{DESTINATION_USERNAME}/{name}") + for name in DATASET_NAMES +] + +# Initialize Hugging Face API +api = HfApi() + +def ensure_repo_exists(repo_id, token): + """Ensure the repository exists, create it if it doesn't""" + try: + api.repo_info(repo_id=repo_id, repo_type="dataset") + logger.info(f"✓ Repository {repo_id} already exists") + except Exception: + logger.info(f"Creating repository {repo_id}...") + create_repo( + repo_id=repo_id, + repo_type="dataset", + token=token, + private=True + ) + logger.info(f"✓ Repository {repo_id} created") + +def process_dataset(dataset_info, token): + """Process a single dataset""" + name, source_dataset, destination_dataset = dataset_info + try: + logger.info(f"\n📥 Processing dataset: {name}") + + # Ensure destination repository exists + ensure_repo_exists(destination_dataset, token) + + # Create a temporary directory for this dataset + with tempfile.TemporaryDirectory() as temp_dir: + try: + # List files in source dataset + logger.info(f"Listing files in {source_dataset}...") + files = api.list_repo_files(source_dataset, repo_type="dataset") + logger.info(f"Detected structure: {len(files)} files") + + # Download dataset + logger.info(f"Downloading from {source_dataset}...") + local_dir = snapshot_download( + repo_id=source_dataset, + repo_type="dataset", + local_dir=temp_dir, + token=token + ) + logger.info(f"✓ Download complete") + + # Upload to destination while preserving structure + logger.info(f"📤 Uploading to {destination_dataset}...") + api.upload_folder( + folder_path=local_dir, + repo_id=destination_dataset, + repo_type="dataset", + token=token + ) + logger.info(f"✅ {name} copied successfully!") + return True + + except Exception as e: + logger.error(f"❌ Error processing {name}: {str(e)}") + return False + + except Exception as e: + logger.error(f"❌ Error for {name}: {str(e)}") + return False + +def copy_datasets(): + try: + logger.info("🔑 Checking authentication...") + # Get token from .env file + token = os.getenv("HF_TOKEN") + if not token: + raise ValueError("HF_TOKEN not found in .env file") + + # Process datasets sequentially + results = [] + for dataset_info in DATASETS: + success = process_dataset(dataset_info, token) + results.append((dataset_info[0], success)) + + # Print final summary + logger.info("\n📊 Final summary:") + for dataset, success in results: + status = "✅ Success" if success else "❌ Failure" + logger.info(f"{dataset}: {status}") + + except Exception as e: + logger.error(f"❌ Global error: {str(e)}") + +if __name__ == "__main__": + copy_datasets() diff --git a/backend/uv.lock b/backend/uv.lock new file mode 100644 index 0000000000000000000000000000000000000000..40e5dae9d4cd0420befa92ec740c2c031349f0a0 --- /dev/null +++ b/backend/uv.lock @@ -0,0 +1,971 @@ +version = 1 +requires-python = "==3.12.1" + +[[package]] +name = "aiofiles" +version = "24.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/55/e4373e888fdacb15563ef6fa9fa8c8252476ea071e96fb46defac9f18bf2/aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", size = 21977 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/74/fbb6559de3607b3300b9be3cc64e97548d55678e44623db17820dbd20002/aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8", size = 14756 }, +] + +[[package]] +name = "aiohttp" +version = "3.11.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/c4/3b5a937b16f6c2a0ada842a9066aad0b7a5708427d4a202a07bf09c67cbb/aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e", size = 7668832 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/17/1dbe2f619f77795409c1a13ab395b98ed1b215d3e938cacde9b8ffdac53d/aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf", size = 704448 }, + { url = "https://files.pythonhosted.org/packages/e3/9b/112247ad47e9d7f6640889c6e42cc0ded8c8345dd0033c66bcede799b051/aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138", size = 463829 }, + { url = "https://files.pythonhosted.org/packages/8a/36/a64b583771fc673062a7a1374728a6241d49e2eda5a9041fbf248e18c804/aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5", size = 455774 }, + { url = "https://files.pythonhosted.org/packages/e5/75/ee1b8f510978b3de5f185c62535b135e4fc3f5a247ca0c2245137a02d800/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50", size = 1682134 }, + { url = "https://files.pythonhosted.org/packages/87/46/65e8259432d5f73ca9ebf5edb645ef90e5303724e4e52477516cb4042240/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c", size = 1736757 }, + { url = "https://files.pythonhosted.org/packages/03/f6/a6d1e791b7153fb2d101278f7146c0771b0e1569c547f8a8bc3035651984/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d", size = 1793033 }, + { url = "https://files.pythonhosted.org/packages/a8/e9/1ac90733e36e7848693aece522936a13bf17eeb617da662f94adfafc1c25/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b", size = 1691609 }, + { url = "https://files.pythonhosted.org/packages/6d/a6/77b33da5a0bc04566c7ddcca94500f2c2a2334eecab4885387fffd1fc600/aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109", size = 1619082 }, + { url = "https://files.pythonhosted.org/packages/48/94/5bf5f927d9a2fedd2c978adfb70a3680e16f46d178361685b56244eb52ed/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab", size = 1641186 }, + { url = "https://files.pythonhosted.org/packages/99/2d/e85103aa01d1064e51bc50cb51e7b40150a8ff5d34e5a3173a46b241860b/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69", size = 1646280 }, + { url = "https://files.pythonhosted.org/packages/7b/e0/44651fda8c1d865a51b3a81f1956ea55ce16fc568fe7a3e05db7fc22f139/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0", size = 1701862 }, + { url = "https://files.pythonhosted.org/packages/4e/1e/0804459ae325a5b95f6f349778fb465f29d2b863e522b6a349db0aaad54c/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9", size = 1734373 }, + { url = "https://files.pythonhosted.org/packages/07/87/b8f6721668cad74bcc9c7cfe6d0230b304d1250196b221e54294a0d78dbe/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc", size = 1694343 }, + { url = "https://files.pythonhosted.org/packages/4b/20/42813fc60d9178ba9b1b86c58a5441ddb6cf8ffdfe66387345bff173bcff/aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985", size = 411118 }, + { url = "https://files.pythonhosted.org/packages/3a/51/df9c263c861ce93998b5ad2ba3212caab2112d5b66dbe91ddbe90c41ded4/aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408", size = 437424 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/40/318e58f669b1a9e00f5c4453910682e2d9dd594334539c7b7817dabb765f/anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", size = 177076 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/7a/4daaf3b6c08ad7ceffea4634ec206faeff697526421c20f07628c7372156/anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352", size = 93052 }, +] + +[[package]] +name = "attrs" +version = "24.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 }, +] + +[[package]] +name = "black" +version = "24.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 }, + { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 }, + { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 }, + { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 }, + { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 }, +] + +[[package]] +name = "certifi" +version = "2024.12.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, + { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, + { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, + { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, + { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, + { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, + { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, + { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, + { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, + { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, + { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, + { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, + { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, + { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, + { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "datasets" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/48/744286c044e2b942d4fa67f92816126522ad1f0675def0ea3264e6242005/datasets-3.2.0.tar.gz", hash = "sha256:9a6e1a356052866b5dbdd9c9eedb000bf3fc43d986e3584d9b028f4976937229", size = 558366 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/84/0df6c5981f5fc722381662ff8cfbdf8aad64bec875f75d80b55bfef394ce/datasets-3.2.0-py3-none-any.whl", hash = "sha256:f3d2ba2698b7284a4518019658596a6a8bc79f31e51516524249d6c59cf0fe2a", size = 480647 }, +] + +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, +] + +[[package]] +name = "fastapi" +version = "0.115.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/72/d83b98cd106541e8f5e5bfab8ef2974ab45a62e8a6c5b5e6940f26d2ed4b/fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654", size = 301336 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/b3/7e4df40e585df024fac2f80d1a2d579c854ac37109675db2b0cc22c0bb9e/fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305", size = 94843 }, +] + +[[package]] +name = "fastapi-cache2" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "pendulum" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/6f/7c2078bf097634276a266fe225d9d6a1f882fe505a662bd1835fb2cf6891/fastapi_cache2-0.2.2.tar.gz", hash = "sha256:71bf4450117dc24224ec120be489dbe09e331143c9f74e75eb6f576b78926026", size = 17950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/b3/ce7c5d9f5e75257a3039ee1e38feb77bee29da3a1792c57d6ea1acb55d17/fastapi_cache2-0.2.2-py3-none-any.whl", hash = "sha256:e1fae86d8eaaa6c8501dfe08407f71d69e87cc6748042d59d51994000532846c", size = 25411 }, +] + +[[package]] +name = "filelock" +version = "3.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, +] + +[[package]] +name = "flake8" +version = "7.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mccabe" }, + { name = "pycodestyle" }, + { name = "pyflakes" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/72/e8d66150c4fcace3c0a450466aa3480506ba2cae7b61e100a2613afc3907/flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", size = 48054 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/42/65004373ac4617464f35ed15931b30d764f53cdd30cc78d5aea349c8c050/flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213", size = 57731 }, +] + +[[package]] +name = "frozenlist" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 }, + { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 }, + { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 }, + { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 }, + { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 }, + { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 }, + { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 }, + { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 }, + { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 }, + { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 }, + { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 }, + { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 }, + { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 }, + { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 }, + { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 }, + { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, +] + +[[package]] +name = "fsspec" +version = "2024.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/7c/12b0943011daaaa9c35c2a2e22e5eb929ac90002f08f1259d69aedad84de/fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8", size = 286206 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/a0/6aaea0c2fbea2f89bfd5db25fb1e3481896a423002ebe4e55288907a97a3/fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b", size = 179253 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/c6/e3709b61de8e7832dbe19f0d9637e81356cede733d99359fbce125423774/huggingface_hub-0.27.0.tar.gz", hash = "sha256:902cce1a1be5739f5589e560198a65a8edcfd3b830b1666f36e4b961f0454fac", size = 379286 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/8c/fbdc0a88a622d9fa54e132d7bf3ee03ec602758658a2db5b339a65be2cfe/huggingface_hub-0.27.0-py3-none-any.whl", hash = "sha256:8f2e834517f1f1ddf1ecc716f91b120d7333011b7485f665a9a412eacb1a2a81", size = 450537 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, +] + +[[package]] +name = "llm-leaderboard-backend" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "aiofiles" }, + { name = "datasets" }, + { name = "fastapi" }, + { name = "fastapi-cache2" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "python-multipart" }, + { name = "safetensors" }, + { name = "transformers" }, + { name = "uvicorn" }, +] + +[package.optional-dependencies] +dev = [ + { name = "black" }, + { name = "flake8" }, + { name = "isort" }, + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofiles", specifier = ">=24.1.0" }, + { name = "black", marker = "extra == 'dev'", specifier = ">=24.10.0" }, + { name = "datasets", specifier = ">=3.2.0" }, + { name = "fastapi", specifier = ">=0.115.6" }, + { name = "fastapi-cache2", specifier = ">=0.2.1" }, + { name = "flake8", marker = "extra == 'dev'", specifier = ">=7.1.1" }, + { name = "huggingface-hub", specifier = ">=0.27.0" }, + { name = "isort", marker = "extra == 'dev'", specifier = ">=5.13.2" }, + { name = "numpy", specifier = ">=2.2.0" }, + { name = "pandas", specifier = ">=2.2.3" }, + { name = "pyarrow", specifier = ">=18.1.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4" }, + { name = "python-multipart", specifier = ">=0.0.20" }, + { name = "safetensors", specifier = ">=0.4.5" }, + { name = "transformers", specifier = ">=4.47.0" }, + { name = "uvicorn", specifier = ">=0.34.0" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, +] + +[[package]] +name = "multidict" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 }, + { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 }, + { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 }, + { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 }, + { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 }, + { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 }, + { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 }, + { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 }, + { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 }, + { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 }, + { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 }, + { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 }, + { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 }, + { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 }, + { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 }, + { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824 }, + { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519 }, + { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741 }, + { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628 }, + { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "numpy" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/1b/1d565e0f6e156e1522ab564176b8b29d71e13d8caf003a08768df3d5cec5/numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0", size = 20225497 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/bc/a20dc4e1d051149052762e7647455311865d11c603170c476d1e910a353e/numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3", size = 20909153 }, + { url = "https://files.pythonhosted.org/packages/60/3d/ac4fb63f36db94f4c7db05b45e3ecb3f88f778ca71850664460c78cfde41/numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e", size = 14095021 }, + { url = "https://files.pythonhosted.org/packages/41/6d/a654d519d24e4fcc7a83d4a51209cda086f26cf30722b3d8ffc1aa9b775e/numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67", size = 5125491 }, + { url = "https://files.pythonhosted.org/packages/e6/22/fab7e1510a62e5092f4e6507a279020052b89f11d9cfe52af7f52c243b04/numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e", size = 6658534 }, + { url = "https://files.pythonhosted.org/packages/fc/29/a3d938ddc5a534cd53df7ab79d20a68db8c67578de1df0ae0118230f5f54/numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038", size = 14046306 }, + { url = "https://files.pythonhosted.org/packages/90/24/d0bbb56abdd8934f30384632e3c2ca1ebfeb5d17e150c6e366ba291de36b/numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03", size = 16095819 }, + { url = "https://files.pythonhosted.org/packages/99/9c/58a673faa9e8a0e77248e782f7a17410cf7259b326265646fd50ed49c4e1/numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a", size = 15243215 }, + { url = "https://files.pythonhosted.org/packages/9c/61/f311693f78cbf635cfb69ce9e1e857ff83937a27d93c96ac5932fd33e330/numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef", size = 17860175 }, + { url = "https://files.pythonhosted.org/packages/11/3e/491c34262cb1fc9dd13a00beb80d755ee0517b17db20e54cac7aa524533e/numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1", size = 6273281 }, + { url = "https://files.pythonhosted.org/packages/89/ea/00537f599eb230771157bc509f6ea5b2dddf05d4b09f9d2f1d7096a18781/numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3", size = 12613227 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, + { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, + { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, + { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, + { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, + { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, + { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "pendulum" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "time-machine", marker = "implementation_name != 'pypy'" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/fe/27c7438c6ac8b8f8bef3c6e571855602ee784b85d072efddfff0ceb1cd77/pendulum-3.0.0.tar.gz", hash = "sha256:5d034998dea404ec31fae27af6b22cff1708f830a1ed7353be4d1019bb9f584e", size = 84524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/37/17c8f0e7481a32f21b9002dd68912a8813f2c1d77b984e00af56eb9ae31b/pendulum-3.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:409e64e41418c49f973d43a28afe5df1df4f1dd87c41c7c90f1a63f61ae0f1f7", size = 362284 }, + { url = "https://files.pythonhosted.org/packages/12/e6/08f462f6ea87e2159f19b43ff88231d26e02bda31c10bcb29290a617ace4/pendulum-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a38ad2121c5ec7c4c190c7334e789c3b4624798859156b138fcc4d92295835dc", size = 352964 }, + { url = "https://files.pythonhosted.org/packages/47/29/b6877f6b53b91356c2c56d19ddab17b165ca994ad1e57b32c089e79f3fb5/pendulum-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fde4d0b2024b9785f66b7f30ed59281bd60d63d9213cda0eb0910ead777f6d37", size = 335848 }, + { url = "https://files.pythonhosted.org/packages/2b/77/62ca666f30b2558342deadda26290a575459a7b59248ea1e978b84175227/pendulum-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2c5675769fb6d4c11238132962939b960fcb365436b6d623c5864287faa319", size = 362215 }, + { url = "https://files.pythonhosted.org/packages/e0/29/ce37593f5ea51862c60dadf4e863d604f954478b3abbcc60a14dc05e242c/pendulum-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8af95e03e066826f0f4c65811cbee1b3123d4a45a1c3a2b4fc23c4b0dff893b5", size = 448673 }, + { url = "https://files.pythonhosted.org/packages/72/6a/68a8c7b8f1977d89aabfd0e2becb0921e5515dfb365097e98a522334a151/pendulum-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2165a8f33cb15e06c67070b8afc87a62b85c5a273e3aaa6bc9d15c93a4920d6f", size = 384891 }, + { url = "https://files.pythonhosted.org/packages/30/e6/edd699300f47a3c53c0d8ed26e905b9a31057c3646211e58cc540716a440/pendulum-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ad5e65b874b5e56bd942546ea7ba9dd1d6a25121db1c517700f1c9de91b28518", size = 559558 }, + { url = "https://files.pythonhosted.org/packages/d4/97/95a44aa5e1763d3a966551ed0e12f56508d8dfcc60e1f0395909b6a08626/pendulum-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17fe4b2c844bbf5f0ece69cfd959fa02957c61317b2161763950d88fed8e13b9", size = 558240 }, + { url = "https://files.pythonhosted.org/packages/9a/91/fcd992eb36b77ab43f2cf44307b72c01a6fbb27f55c1bb2d4af30e9a6cb7/pendulum-3.0.0-cp312-none-win_amd64.whl", hash = "sha256:78f8f4e7efe5066aca24a7a57511b9c2119f5c2b5eb81c46ff9222ce11e0a7a5", size = 293456 }, + { url = "https://files.pythonhosted.org/packages/3b/60/ba8aa296ca6d76603d58146b4a222cd99e7da33831158b8c00240a896a56/pendulum-3.0.0-cp312-none-win_arm64.whl", hash = "sha256:28f49d8d1e32aae9c284a90b6bb3873eee15ec6e1d9042edd611b22a94ac462f", size = 288054 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "propcache" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/28/1d205fe49be8b1b4df4c50024e62480a442b1a7b818e734308bb0d17e7fb/propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", size = 79588 }, + { url = "https://files.pythonhosted.org/packages/21/ee/fc4d893f8d81cd4971affef2a6cb542b36617cd1d8ce56b406112cb80bf7/propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", size = 45825 }, + { url = "https://files.pythonhosted.org/packages/4a/de/bbe712f94d088da1d237c35d735f675e494a816fd6f54e9db2f61ef4d03f/propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", size = 45357 }, + { url = "https://files.pythonhosted.org/packages/7f/14/7ae06a6cf2a2f1cb382586d5a99efe66b0b3d0c6f9ac2f759e6f7af9d7cf/propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", size = 241869 }, + { url = "https://files.pythonhosted.org/packages/cc/59/227a78be960b54a41124e639e2c39e8807ac0c751c735a900e21315f8c2b/propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", size = 247884 }, + { url = "https://files.pythonhosted.org/packages/84/58/f62b4ffaedf88dc1b17f04d57d8536601e4e030feb26617228ef930c3279/propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", size = 248486 }, + { url = "https://files.pythonhosted.org/packages/1c/07/ebe102777a830bca91bbb93e3479cd34c2ca5d0361b83be9dbd93104865e/propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", size = 243649 }, + { url = "https://files.pythonhosted.org/packages/ed/bc/4f7aba7f08f520376c4bb6a20b9a981a581b7f2e385fa0ec9f789bb2d362/propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", size = 229103 }, + { url = "https://files.pythonhosted.org/packages/fe/d5/04ac9cd4e51a57a96f78795e03c5a0ddb8f23ec098b86f92de028d7f2a6b/propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", size = 226607 }, + { url = "https://files.pythonhosted.org/packages/e3/f0/24060d959ea41d7a7cc7fdbf68b31852331aabda914a0c63bdb0e22e96d6/propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", size = 221153 }, + { url = "https://files.pythonhosted.org/packages/77/a7/3ac76045a077b3e4de4859a0753010765e45749bdf53bd02bc4d372da1a0/propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", size = 222151 }, + { url = "https://files.pythonhosted.org/packages/e7/af/5e29da6f80cebab3f5a4dcd2a3240e7f56f2c4abf51cbfcc99be34e17f0b/propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", size = 233812 }, + { url = "https://files.pythonhosted.org/packages/8c/89/ebe3ad52642cc5509eaa453e9f4b94b374d81bae3265c59d5c2d98efa1b4/propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", size = 238829 }, + { url = "https://files.pythonhosted.org/packages/e9/2f/6b32f273fa02e978b7577159eae7471b3cfb88b48563b1c2578b2d7ca0bb/propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", size = 230704 }, + { url = "https://files.pythonhosted.org/packages/5c/2e/f40ae6ff5624a5f77edd7b8359b208b5455ea113f68309e2b00a2e1426b6/propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", size = 40050 }, + { url = "https://files.pythonhosted.org/packages/3b/77/a92c3ef994e47180862b9d7d11e37624fb1c00a16d61faf55115d970628b/propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", size = 44117 }, + { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 }, +] + +[[package]] +name = "pyarrow" +version = "18.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/7b/640785a9062bb00314caa8a387abce547d2a420cf09bd6c715fe659ccffb/pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73", size = 1118671 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/50/12829e7111b932581e51dda51d5cb39207a056c30fe31ef43f14c63c4d7e/pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d", size = 29514620 }, + { url = "https://files.pythonhosted.org/packages/d1/41/468c944eab157702e96abab3d07b48b8424927d4933541ab43788bb6964d/pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee", size = 30856494 }, + { url = "https://files.pythonhosted.org/packages/68/f9/29fb659b390312a7345aeb858a9d9c157552a8852522f2c8bad437c29c0a/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992", size = 39203624 }, + { url = "https://files.pythonhosted.org/packages/6e/f6/19360dae44200e35753c5c2889dc478154cd78e61b1f738514c9f131734d/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54", size = 40139341 }, + { url = "https://files.pythonhosted.org/packages/bb/e6/9b3afbbcf10cc724312e824af94a2e993d8ace22994d823f5c35324cebf5/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33", size = 38618629 }, + { url = "https://files.pythonhosted.org/packages/3a/2e/3b99f8a3d9e0ccae0e961978a0d0089b25fb46ebbcfb5ebae3cca179a5b3/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30", size = 40078661 }, + { url = "https://files.pythonhosted.org/packages/76/52/f8da04195000099d394012b8d42c503d7041b79f778d854f410e5f05049a/pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99", size = 25092330 }, +] + +[[package]] +name = "pycodestyle" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/aa/210b2c9aedd8c1cbeea31a50e42050ad56187754b34eb214c46709445801/pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521", size = 39232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/d8/a211b3f85e99a0daa2ddec96c949cac6824bd305b040571b82a03dd62636/pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", size = 31284 }, +] + +[[package]] +name = "pydantic" +version = "2.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/0f/27908242621b14e649a84e62b133de45f84c255eecb350ab02979844a788/pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9", size = 786486 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/51/72c18c55cf2f46ff4f91ebcc8f75aa30f7305f3d726be3f4ebffb4ae972b/pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", size = 456997 }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/9f/7de1f19b6aea45aeb441838782d68352e71bfa98ee6fa048d5041991b33e/pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", size = 412785 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/51/2e9b3788feb2aebff2aa9dfbf060ec739b38c05c46847601134cc1fed2ea/pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", size = 1895239 }, + { url = "https://files.pythonhosted.org/packages/7b/9e/f8063952e4a7d0127f5d1181addef9377505dcce3be224263b25c4f0bfd9/pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", size = 1805070 }, + { url = "https://files.pythonhosted.org/packages/2c/9d/e1d6c4561d262b52e41b17a7ef8301e2ba80b61e32e94520271029feb5d8/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", size = 1828096 }, + { url = "https://files.pythonhosted.org/packages/be/65/80ff46de4266560baa4332ae3181fffc4488ea7d37282da1a62d10ab89a4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", size = 1857708 }, + { url = "https://files.pythonhosted.org/packages/d5/ca/3370074ad758b04d9562b12ecdb088597f4d9d13893a48a583fb47682cdf/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", size = 2037751 }, + { url = "https://files.pythonhosted.org/packages/b1/e2/4ab72d93367194317b99d051947c071aef6e3eb95f7553eaa4208ecf9ba4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", size = 2733863 }, + { url = "https://files.pythonhosted.org/packages/8a/c6/8ae0831bf77f356bb73127ce5a95fe115b10f820ea480abbd72d3cc7ccf3/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", size = 2161161 }, + { url = "https://files.pythonhosted.org/packages/f1/f4/b2fe73241da2429400fc27ddeaa43e35562f96cf5b67499b2de52b528cad/pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", size = 1993294 }, + { url = "https://files.pythonhosted.org/packages/77/29/4bb008823a7f4cc05828198153f9753b3bd4c104d93b8e0b1bfe4e187540/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", size = 2001468 }, + { url = "https://files.pythonhosted.org/packages/f2/a9/0eaceeba41b9fad851a4107e0cf999a34ae8f0d0d1f829e2574f3d8897b0/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", size = 2091413 }, + { url = "https://files.pythonhosted.org/packages/d8/36/eb8697729725bc610fd73940f0d860d791dc2ad557faaefcbb3edbd2b349/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", size = 2154735 }, + { url = "https://files.pythonhosted.org/packages/52/e5/4f0fbd5c5995cc70d3afed1b5c754055bb67908f55b5cb8000f7112749bf/pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", size = 1833633 }, + { url = "https://files.pythonhosted.org/packages/ee/f2/c61486eee27cae5ac781305658779b4a6b45f9cc9d02c90cb21b940e82cc/pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", size = 1986973 }, + { url = "https://files.pythonhosted.org/packages/df/a6/e3f12ff25f250b02f7c51be89a294689d175ac76e1096c32bf278f29ca1e/pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", size = 1883215 }, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 }, +] + +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, +] + +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "safetensors" +version = "0.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/46/a1c56ed856c6ac3b1a8b37abe5be0cac53219367af1331e721b04d122577/safetensors-0.4.5.tar.gz", hash = "sha256:d73de19682deabb02524b3d5d1f8b3aaba94c72f1bbfc7911b9b9d5d391c0310", size = 65702 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/ac/5a63082f931e99200db95fd46fb6734f050bb6e96bf02521904c6518b7aa/safetensors-0.4.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:473300314e026bd1043cef391bb16a8689453363381561b8a3e443870937cc1e", size = 392015 }, + { url = "https://files.pythonhosted.org/packages/73/95/ab32aa6e9bdc832ff87784cdf9da26192b93de3ef82b8d1ada8f345c5044/safetensors-0.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:801183a0f76dc647f51a2d9141ad341f9665602a7899a693207a82fb102cc53e", size = 381774 }, + { url = "https://files.pythonhosted.org/packages/d6/6c/7e04b7626809fc63f3698f4c50e43aff2864b40089aa4506c918a75b8eed/safetensors-0.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1524b54246e422ad6fb6aea1ac71edeeb77666efa67230e1faf6999df9b2e27f", size = 441134 }, + { url = "https://files.pythonhosted.org/packages/58/2b/ffe7c86a277e6c1595fbdf415cfe2903f253f574a5405e93fda8baaa582c/safetensors-0.4.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3139098e3e8b2ad7afbca96d30ad29157b50c90861084e69fcb80dec7430461", size = 438467 }, + { url = "https://files.pythonhosted.org/packages/67/9c/f271bd804e08c7fda954d17b70ff281228a88077337a9e70feace4f4cc93/safetensors-0.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65573dc35be9059770808e276b017256fa30058802c29e1038eb1c00028502ea", size = 476566 }, + { url = "https://files.pythonhosted.org/packages/4c/ad/4cf76a3e430a8a26108407fa6cb93e6f80d996a5cb75d9540c8fe3862990/safetensors-0.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd33da8e9407559f8779c82a0448e2133737f922d71f884da27184549416bfed", size = 492253 }, + { url = "https://files.pythonhosted.org/packages/d9/40/a6f75ea449a9647423ec8b6f72c16998d35aa4b43cb38536ac060c5c7bf5/safetensors-0.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3685ce7ed036f916316b567152482b7e959dc754fcc4a8342333d222e05f407c", size = 434769 }, + { url = "https://files.pythonhosted.org/packages/52/47/d4b49b1231abf3131f7bb0bc60ebb94b27ee33e0a1f9569da05f8ac65dee/safetensors-0.4.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dde2bf390d25f67908278d6f5d59e46211ef98e44108727084d4637ee70ab4f1", size = 457166 }, + { url = "https://files.pythonhosted.org/packages/c3/cd/006468b03b0fa42ff82d795d47c4193e99001e96c3f08bd62ef1b5cab586/safetensors-0.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7469d70d3de970b1698d47c11ebbf296a308702cbaae7fcb993944751cf985f4", size = 619280 }, + { url = "https://files.pythonhosted.org/packages/22/4d/b6208d918e83daa84b424c0ac3191ae61b44b3191613a3a5a7b38f94b8ad/safetensors-0.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a6ba28118636a130ccbb968bc33d4684c48678695dba2590169d5ab03a45646", size = 605390 }, + { url = "https://files.pythonhosted.org/packages/e8/20/bf0e01825dc01ed75538021a98b9a046e60ead63c6c6700764c821a8c873/safetensors-0.4.5-cp312-none-win32.whl", hash = "sha256:c859c7ed90b0047f58ee27751c8e56951452ed36a67afee1b0a87847d065eec6", size = 273250 }, + { url = "https://files.pythonhosted.org/packages/f1/5f/ab6b6cec85b40789801f35b7d2fb579ae242d8193929974a106d5ff5c835/safetensors-0.4.5-cp312-none-win_amd64.whl", hash = "sha256:b5a8810ad6a6f933fff6c276eae92c1da217b39b4d8b1bc1c0b8af2d270dc532", size = 286307 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "starlette" +version = "0.41.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/4c/9b5764bd22eec91c4039ef4c55334e9187085da2d8a2df7bd570869aae18/starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", size = 2574159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, +] + +[[package]] +name = "time-machine" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/dd/5022939b9cadefe3af04f4012186c29b8afbe858b1ec2cfa38baeec94dab/time_machine-2.16.0.tar.gz", hash = "sha256:4a99acc273d2f98add23a89b94d4dd9e14969c01214c8514bfa78e4e9364c7e2", size = 24626 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/f4/603a84e7ae6427a53953db9f61b689dc6adf233e03c5f5ca907a901452fd/time_machine-2.16.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:84788f4d62a8b1bf5e499bb9b0e23ceceea21c415ad6030be6267ce3d639842f", size = 20155 }, + { url = "https://files.pythonhosted.org/packages/d8/94/dbe69aecb4b84be52d34814e63176c5ca61f38ee9e6ecda11104653405b5/time_machine-2.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:15ec236b6571730236a193d9d6c11d472432fc6ab54e85eac1c16d98ddcd71bf", size = 16640 }, + { url = "https://files.pythonhosted.org/packages/da/13/27f11be25d7bd298e033b9da93217e5b68309bf724b6e494cdadb471d00d/time_machine-2.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cedc989717c8b44a3881ac3d68ab5a95820448796c550de6a2149ed1525157f0", size = 33721 }, + { url = "https://files.pythonhosted.org/packages/e6/9d/70e4640fed1fd8122204ae825c688d0ef8c04f515ec6bf3c5f3086d6510e/time_machine-2.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d26d79de1c63a8c6586c75967e09b0ff306aa7e944a1eaddb74595c9b1839ca", size = 31646 }, + { url = "https://files.pythonhosted.org/packages/a1/cb/93bc0e51bea4e171a85151dbba3c3b3f612b50b953cd3076f5b4f0db9e14/time_machine-2.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:317b68b56a9c3731e0cf8886e0f94230727159e375988b36c60edce0ddbcb44a", size = 33403 }, + { url = "https://files.pythonhosted.org/packages/89/71/2c6a63ad4fbce3d62d46bbd9ac4433f30bade7f25978ce00815b905bcfcf/time_machine-2.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:43e1e18279759897be3293a255d53e6b1cb0364b69d9591d0b80c51e461c94b0", size = 33327 }, + { url = "https://files.pythonhosted.org/packages/68/4e/205c2b26763b8817cd6b8868242843800a1fbf275f2af35f5ba35ff2b01a/time_machine-2.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e43adb22def972a29d2b147999b56897116085777a0fea182fd93ee45730611e", size = 31454 }, + { url = "https://files.pythonhosted.org/packages/d7/95/44c1aa3994919f93534244c40cfd2fb9416d7686dc0c8b9b262c751b5118/time_machine-2.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0c766bea27a0600e36806d628ebc4b47178b12fcdfb6c24dc0a566a9c06bfe7f", size = 32972 }, + { url = "https://files.pythonhosted.org/packages/d4/ee/75243df9c7cf30f108758e887141a58e6544baaa46e2e647b9ccc56db819/time_machine-2.16.0-cp312-cp312-win32.whl", hash = "sha256:6dae82ab647d107817e013db82223e20a9853fa88543fec853ae326382d03c2e", size = 19078 }, + { url = "https://files.pythonhosted.org/packages/d4/7c/d4e67cc031f9653c92167ccf87d241e3208653d191c96ac79281c273ab92/time_machine-2.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:265462c77dc9576267c3c7f20707780a171a9fdbac93ac22e608c309efd68c33", size = 19923 }, + { url = "https://files.pythonhosted.org/packages/aa/b6/7047226fcb9afefe47fc80f605530535bf71ad99b6797f057abbfa4cd9a5/time_machine-2.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:ef768e14768eebe3bb1196c0dece8e14c1c6991605721214a0c3c68cf77eb216", size = 18003 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/41/c2be10975ca37f6ec40d7abd7e98a5213bb04f284b869c1a24e6504fd94d/tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4", size = 343021 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/5c/8b09607b37e996dc47e70d6a7b6f4bdd4e4d5ab22fe49d7374565c7fefaf/tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2", size = 2647461 }, + { url = "https://files.pythonhosted.org/packages/22/7a/88e58bb297c22633ed1c9d16029316e5b5ac5ee44012164c2edede599a5e/tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e", size = 2563639 }, + { url = "https://files.pythonhosted.org/packages/f7/14/83429177c19364df27d22bc096d4c2e431e0ba43e56c525434f1f9b0fd00/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193", size = 2903304 }, + { url = "https://files.pythonhosted.org/packages/7e/db/3433eab42347e0dc5452d8fcc8da03f638c9accffefe5a7c78146666964a/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e", size = 2804378 }, + { url = "https://files.pythonhosted.org/packages/57/8b/7da5e6f89736c2ade02816b4733983fca1c226b0c42980b1ae9dc8fcf5cc/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e", size = 3095488 }, + { url = "https://files.pythonhosted.org/packages/4d/f6/5ed6711093dc2c04a4e03f6461798b12669bc5a17c8be7cce1240e0b5ce8/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba", size = 3121410 }, + { url = "https://files.pythonhosted.org/packages/81/42/07600892d48950c5e80505b81411044a2d969368cdc0d929b1c847bf6697/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273", size = 3388821 }, + { url = "https://files.pythonhosted.org/packages/22/06/69d7ce374747edaf1695a4f61b83570d91cc8bbfc51ccfecf76f56ab4aac/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04", size = 3008868 }, + { url = "https://files.pythonhosted.org/packages/c8/69/54a0aee4d576045b49a0eb8bffdc495634309c823bf886042e6f46b80058/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e", size = 8975831 }, + { url = "https://files.pythonhosted.org/packages/f7/f3/b776061e4f3ebf2905ba1a25d90380aafd10c02d406437a8ba22d1724d76/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b", size = 8920746 }, + { url = "https://files.pythonhosted.org/packages/d8/ee/ce83d5ec8b6844ad4c3ecfe3333d58ecc1adc61f0878b323a15355bcab24/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74", size = 9161814 }, + { url = "https://files.pythonhosted.org/packages/18/07/3e88e65c0ed28fa93aa0c4d264988428eef3df2764c3126dc83e243cb36f/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff", size = 9357138 }, + { url = "https://files.pythonhosted.org/packages/15/b0/dc4572ca61555fc482ebc933f26cb407c6aceb3dc19c301c68184f8cad03/tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a", size = 2202266 }, + { url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.47.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/5a/0ecfde3264bed0579c37f249e04e15f3c1451ba864d78bbe390177664cac/transformers-4.47.0.tar.gz", hash = "sha256:f8ead7a5a4f6937bb507e66508e5e002dc5930f7b6122a9259c37b099d0f3b19", size = 8693668 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/a7/7eedcf6a359e1e1eff3bc204ad022485aa5d88c08e1e3e0e0aee8a2e2235/transformers-4.47.0-py3-none-any.whl", hash = "sha256:a8e1bafdaae69abdda3cad638fe392e37c86d2ce0ecfcae11d60abb8f949ff4d", size = 10133426 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "uvicorn" +version = "0.34.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787 }, + { url = "https://files.pythonhosted.org/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959 }, + { url = "https://files.pythonhosted.org/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006 }, + { url = "https://files.pythonhosted.org/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326 }, + { url = "https://files.pythonhosted.org/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380 }, + { url = "https://files.pythonhosted.org/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934 }, + { url = "https://files.pythonhosted.org/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301 }, + { url = "https://files.pythonhosted.org/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351 }, + { url = "https://files.pythonhosted.org/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294 }, + { url = "https://files.pythonhosted.org/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674 }, + { url = "https://files.pythonhosted.org/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022 }, + { url = "https://files.pythonhosted.org/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170 }, + { url = "https://files.pythonhosted.org/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040 }, + { url = "https://files.pythonhosted.org/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796 }, +] + +[[package]] +name = "yarl" +version = "1.18.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/85/bd2e2729752ff4c77338e0102914897512e92496375e079ce0150a6dc306/yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", size = 142644 }, + { url = "https://files.pythonhosted.org/packages/ff/74/1178322cc0f10288d7eefa6e4a85d8d2e28187ccab13d5b844e8b5d7c88d/yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", size = 94962 }, + { url = "https://files.pythonhosted.org/packages/be/75/79c6acc0261e2c2ae8a1c41cf12265e91628c8c58ae91f5ff59e29c0787f/yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", size = 92795 }, + { url = "https://files.pythonhosted.org/packages/6b/32/927b2d67a412c31199e83fefdce6e645247b4fb164aa1ecb35a0f9eb2058/yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", size = 332368 }, + { url = "https://files.pythonhosted.org/packages/19/e5/859fca07169d6eceeaa4fde1997c91d8abde4e9a7c018e371640c2da2b71/yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", size = 342314 }, + { url = "https://files.pythonhosted.org/packages/08/75/76b63ccd91c9e03ab213ef27ae6add2e3400e77e5cdddf8ed2dbc36e3f21/yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", size = 341987 }, + { url = "https://files.pythonhosted.org/packages/1a/e1/a097d5755d3ea8479a42856f51d97eeff7a3a7160593332d98f2709b3580/yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", size = 336914 }, + { url = "https://files.pythonhosted.org/packages/0b/42/e1b4d0e396b7987feceebe565286c27bc085bf07d61a59508cdaf2d45e63/yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", size = 325765 }, + { url = "https://files.pythonhosted.org/packages/7e/18/03a5834ccc9177f97ca1bbb245b93c13e58e8225276f01eedc4cc98ab820/yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", size = 344444 }, + { url = "https://files.pythonhosted.org/packages/c8/03/a713633bdde0640b0472aa197b5b86e90fbc4c5bc05b727b714cd8a40e6d/yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", size = 340760 }, + { url = "https://files.pythonhosted.org/packages/eb/99/f6567e3f3bbad8fd101886ea0276c68ecb86a2b58be0f64077396cd4b95e/yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", size = 346484 }, + { url = "https://files.pythonhosted.org/packages/8e/a9/84717c896b2fc6cb15bd4eecd64e34a2f0a9fd6669e69170c73a8b46795a/yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", size = 359864 }, + { url = "https://files.pythonhosted.org/packages/1e/2e/d0f5f1bef7ee93ed17e739ec8dbcb47794af891f7d165fa6014517b48169/yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", size = 364537 }, + { url = "https://files.pythonhosted.org/packages/97/8a/568d07c5d4964da5b02621a517532adb8ec5ba181ad1687191fffeda0ab6/yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", size = 357861 }, + { url = "https://files.pythonhosted.org/packages/7d/e3/924c3f64b6b3077889df9a1ece1ed8947e7b61b0a933f2ec93041990a677/yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", size = 84097 }, + { url = "https://files.pythonhosted.org/packages/34/45/0e055320daaabfc169b21ff6174567b2c910c45617b0d79c68d7ab349b02/yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", size = 90399 }, + { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 }, +] diff --git a/contents.py b/contents.py new file mode 100644 index 0000000000000000000000000000000000000000..f00bed99ca9421511bdc3eb6f5bfeb6424523989 --- /dev/null +++ b/contents.py @@ -0,0 +1,6 @@ +from datasets import load_dataset + +ds = load_dataset("open-llm-leaderboard/contents") +ds["train"] = ds["train"].select([0]) +print (ds["train"][0]) +# ds.push_to_hub("TheFinAI/greek-contents") diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..f2893d3eef1fc6b5af918d0debd30e9c45de5733 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +services: + backend: + build: + context: ./backend + dockerfile: Dockerfile.dev + args: + - HF_TOKEN=${HF_TOKEN} + ports: + - "${BACKEND_PORT:-8000}:8000" + volumes: + - ./backend:/app + environment: + - ENVIRONMENT=${ENVIRONMENT:-development} + - HF_TOKEN=${HF_TOKEN} + - HF_HOME=${HF_HOME:-/.cache} + command: uvicorn app.asgi:app --host 0.0.0.0 --port 8000 --reload + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.dev + ports: + - "${FRONTEND_PORT:-7860}:7860" + volumes: + - ./frontend:/app + - /app/node_modules + environment: + - NODE_ENV=${ENVIRONMENT:-development} + - CHOKIDAR_USEPOLLING=true + - PORT=${FRONTEND_PORT:-7860} + command: npm start + stdin_open: true + tty: true \ No newline at end of file diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev new file mode 100644 index 0000000000000000000000000000000000000000..259f7c9d8746db26bee8ee531d96cbe0d619321e --- /dev/null +++ b/frontend/Dockerfile.dev @@ -0,0 +1,15 @@ +FROM node:18 + +WORKDIR /app + +# Install required global dependencies +RUN npm install -g react-scripts + +# Copy package.json and package-lock.json +COPY package*.json ./ + +# Install project dependencies +RUN npm install + +# Volume will be mounted here, no need for COPY +CMD ["npm", "start"] \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7ef4ff265f3c870efce128f47bdda8d266689a88 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,80 @@ +# Frontend - Open LLM Leaderboard 🏆 + +React interface for exploring and comparing open-source language models. + +## 🏗 Architecture + +```mermaid +flowchart TD + Client(["User Browser"]) --> Components["React Components"] + + subgraph Frontend + Components --> Context["Context Layer
• LeaderboardContext
• Global State"] + + API["API Layer
• /api/leaderboard/formatted
• TanStack Query"] --> |Data Feed| Context + + Context --> Hooks["Hooks Layer
• Data Processing
• Filtering
• Caching"] + + Hooks --> Features["Features
• Table Management
• Search & Filters
• Display Options"] + Features --> Cache["Cache Layer
• LocalStorage
• URL State"] + end + + API --> Backend["Backend Server"] + + style Backend fill:#f96,stroke:#333,stroke-width:2px +``` + +## ✨ Core Features + +- 🔍 **Search & Filters**: Real-time filtering, regex search, advanced filters +- 📊 **Data Visualization**: Interactive table, customizable columns, sorting +- 🔄 **State Management**: URL sync, client-side caching (5min TTL) +- 📱 **Responsive Design**: Mobile-friendly, dark/light themes + +## 🛠 Tech Stack + +- React 18 + Material-UI +- TanStack Query & Table +- React Router v6 + +## 📁 Project Structure + +``` +src/ +├── pages/ +│ └── LeaderboardPage/ +│ ├── components/ # UI Components +│ ├── context/ # Global State +│ └── hooks/ # Data Processing +├── components/ # Shared Components +└── utils/ # Helper Functions +``` + +## 🚀 Development + +```bash +# Install dependencies +npm install + +# Start development server +npm start + +# Production build +npm run build +``` + +## 🔧 Environment Variables + +```env +# API Configuration +REACT_APP_API_URL=http://localhost:8000 +REACT_APP_CACHE_DURATION=300000 # 5 minutes +``` + +## 🔄 Data Flow + +1. API fetches leaderboard data from backend +2. Context stores and manages global state +3. Hooks handle data processing and filtering +4. Components render based on processed data +5. Cache maintains user preferences and URL state diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000000000000000000000000000000000000..eeaddcd65f200be794c9d57ebf700eafdab27c72 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,55 @@ +{ + "name": "open-finllm-leaderboard", + "version": "0.1.0", + "private": true, + "dependencies": { + "@emotion/react": "^11.13.3", + "@emotion/styled": "^11.13.0", + "@huggingface/hub": "^0.14.0", + "@mui/icons-material": "^6.1.7", + "@mui/lab": "^6.0.0-beta.16", + "@mui/material": "^6.1.6", + "@mui/x-data-grid": "^7.22.2", + "@tanstack/react-query": "^5.62.2", + "@tanstack/react-table": "^8.20.5", + "@tanstack/react-virtual": "^3.10.9", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0", + "react-scripts": "5.0.1", + "serve-static": "^1.15.0", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject", + "serve": "node server.js" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "proxy": "http://backend:8000" +} diff --git a/frontend/server.js b/frontend/server.js new file mode 100644 index 0000000000000000000000000000000000000000..653befea69419568b117ce809871639d86d65581 --- /dev/null +++ b/frontend/server.js @@ -0,0 +1,85 @@ +const express = require("express"); +const cors = require("cors"); +const compression = require("compression"); +const path = require("path"); +const serveStatic = require("serve-static"); +const { createProxyMiddleware } = require("http-proxy-middleware"); + +const app = express(); +const port = process.env.PORT || 7860; +const apiPort = process.env.INTERNAL_API_PORT || 7861; + +// Enable CORS for all routes +app.use(cors()); + +// Enable GZIP compression +app.use(compression()); + +// Proxy all API requests to the Python backend +app.use( + "/api", + createProxyMiddleware({ + target: `http://127.0.0.1:${apiPort}`, + changeOrigin: true, + onError: (err, req, res) => { + console.error("Proxy Error:", err); + res.status(500).json({ error: "Proxy Error", details: err.message }); + }, + }) +); + +// Serve static files from the build directory +app.use( + express.static(path.join(__dirname, "build"), { + // Don't cache HTML files + setHeaders: (res, path) => { + if (path.endsWith(".html")) { + res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + res.setHeader("Pragma", "no-cache"); + res.setHeader("Expires", "0"); + } else { + // Cache other static resources for 1 year + res.setHeader("Cache-Control", "public, max-age=31536000"); + } + }, + }) +); + +// Middleware to preserve URL parameters +app.use((req, res, next) => { + // Don't interfere with API requests + if (req.url.startsWith("/api")) { + return next(); + } + + // Preserve original URL parameters + req.originalUrl = req.url; + next(); +}); + +// Handle all other routes by serving index.html +app.get("*", (req, res) => { + // Don't interfere with API requests + if (req.url.startsWith("/api")) { + return next(); + } + + // Headers for client-side routing + res.set({ + "Cache-Control": "no-cache, no-store, must-revalidate", + Pragma: "no-cache", + Expires: "0", + }); + + // Send index.html for all other routes + res.sendFile(path.join(__dirname, "build", "index.html")); +}); + +app.listen(port, "0.0.0.0", () => { + console.log( + `Frontend server is running on port ${port} in ${ + process.env.NODE_ENV || "development" + } mode` + ); + console.log(`API proxy target: http://127.0.0.1:${apiPort}`); +}); diff --git a/frontend/src/App.js b/frontend/src/App.js new file mode 100644 index 0000000000000000000000000000000000000000..2c406222601bba8f7fe2173564438908e55d5760 --- /dev/null +++ b/frontend/src/App.js @@ -0,0 +1,125 @@ +import React, { useEffect } from "react"; +import { + HashRouter as Router, + Routes, + Route, + useSearchParams, + useLocation, +} from "react-router-dom"; +import { ThemeProvider } from "@mui/material/styles"; +import { Box, CssBaseline } from "@mui/material"; +import Navigation from "./components/Navigation/Navigation"; +import LeaderboardPage from "./pages/LeaderboardPage/LeaderboardPage"; +import AddModelPage from "./pages/AddModelPage/AddModelPage"; +import QuotePage from "./pages/QuotePage/QuotePage"; +import VoteModelPage from "./pages/VoteModelPage/VoteModelPage"; +import Footer from "./components/Footer/Footer"; +import getTheme from "./config/theme"; +import { useThemeMode } from "./hooks/useThemeMode"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import LeaderboardProvider from "./pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + refetchOnWindowFocus: false, + }, + }, +}); + +function UrlHandler() { + const location = useLocation(); + const [searchParams] = useSearchParams(); + + // Synchroniser l'URL avec la page parente HF + useEffect(() => { + // Vérifier si nous sommes dans un iframe HF Space + const isHFSpace = window.location !== window.parent.location; + if (!isHFSpace) return; + + // Sync query and hash from this embedded app to the parent page URL + const queryString = window.location.search; + const hash = window.location.hash; + + // HF Spaces' special message type to update the query string and the hash in the parent page URL + window.parent.postMessage( + { + queryString, + hash, + }, + "https://huggingface.co" + ); + }, [location, searchParams]); + + // Read the updated hash reactively + useEffect(() => { + const handleHashChange = (event) => { + console.log("hash change event", event); + }; + + window.addEventListener("hashchange", handleHashChange); + return () => window.removeEventListener("hashchange", handleHashChange); + }, []); + + return null; +} + +function App() { + const { mode, toggleTheme } = useThemeMode(); + const theme = getTheme(mode); + + return ( +
+ + + + + + + + + + + } /> + } /> + } /> + } /> + + +
+ + + + + +
+ ); +} + +export default App; diff --git a/frontend/src/components/Footer/Footer.js b/frontend/src/components/Footer/Footer.js new file mode 100644 index 0000000000000000000000000000000000000000..c0f559a9206e0cd856a5d752de4413ecedcaeb88 --- /dev/null +++ b/frontend/src/components/Footer/Footer.js @@ -0,0 +1,57 @@ +import React from "react"; +import { Box, Typography, Link, Stack } from "@mui/material"; + +const Footer = () => { + const currentYear = new Date().getFullYear(); + + return ( + + + + © {currentYear} The Fin AI - Open Financial LLM Leaderboard + + + + Home + + + GitHub + + + Documentation + + + + + ); +}; + +export default Footer; diff --git a/frontend/src/components/Logo/HFLogo.js b/frontend/src/components/Logo/HFLogo.js new file mode 100644 index 0000000000000000000000000000000000000000..e49263da5f52e62f50db806f6f295d94e75be47f --- /dev/null +++ b/frontend/src/components/Logo/HFLogo.js @@ -0,0 +1,19 @@ +import React from 'react'; + +const HFLogo = () => ( + + hg-logo + + +); + +export default HFLogo; \ No newline at end of file diff --git a/frontend/src/components/Logo/Logo.js b/frontend/src/components/Logo/Logo.js new file mode 100644 index 0000000000000000000000000000000000000000..6363c901c2cdd9d3c60c1116eb283e4603cae75f --- /dev/null +++ b/frontend/src/components/Logo/Logo.js @@ -0,0 +1,98 @@ +import React from "react"; +import { useNavigate, useSearchParams, useLocation } from "react-router-dom"; +import { Box } from "@mui/material"; +import HFLogo from "./HFLogo"; +import { useLeaderboard } from "../../pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext"; + +const Logo = ({ height = "40px" }) => { + const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); + const location = useLocation(); + const { actions } = useLeaderboard(); + + const handleReset = () => { + // Reset all leaderboard state first + actions.resetAll(); + + // Then clean URL in one go + if ( + location.pathname !== "/" || + searchParams.toString() !== "" || + location.hash !== "" + ) { + window.history.replaceState(null, "", "/"); + navigate("/", { replace: true, state: { skipUrlSync: true } }); + setSearchParams({}, { replace: true, state: { skipUrlSync: true } }); + } + }; + + return ( + + + + + + + + ); +}; + +export default Logo; diff --git a/frontend/src/components/Navigation/Navigation.js b/frontend/src/components/Navigation/Navigation.js new file mode 100644 index 0000000000000000000000000000000000000000..dd7da909c7a41a7f2e55fb378fd5cd93720d58b8 --- /dev/null +++ b/frontend/src/components/Navigation/Navigation.js @@ -0,0 +1,414 @@ +import React, { useState } from "react"; +import { + AppBar, + Toolbar, + Box, + Link as MuiLink, + IconButton, + Tooltip, + ButtonBase, + Typography, +} from "@mui/material"; +import { useLocation, useNavigate, useSearchParams } from "react-router-dom"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; +import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined"; +import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined"; +import { alpha } from "@mui/material/styles"; +import MenuIcon from "@mui/icons-material/Menu"; +import { Menu, MenuItem, useMediaQuery, useTheme } from "@mui/material"; + +const Navigation = ({ onToggleTheme, mode }) => { + const location = useLocation(); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const [anchorEl, setAnchorEl] = useState(null); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("md")); + const [hasChanged, setHasChanged] = useState(false); + + const handleThemeToggle = () => { + setHasChanged(true); + onToggleTheme(); + }; + + const iconStyle = { + fontSize: "1.125rem", + ...(hasChanged && { + animation: "rotateIn 0.3s cubic-bezier(0.4, 0, 0.2, 1)", + "@keyframes rotateIn": { + "0%": { + opacity: 0, + transform: + mode === "light" + ? "rotate(-90deg) scale(0.8)" + : "rotate(90deg) scale(0.8)", + }, + "100%": { + opacity: 1, + transform: "rotate(0) scale(1)", + }, + }, + }), + }; + + // Function to sync URL with parent HF page + const syncUrlWithParent = (queryString, hash) => { + // Check if we're in an HF Space iframe + const isHFSpace = window.location !== window.parent.location; + if (isHFSpace) { + try { + // Build complete URL with hash + const fullPath = `${queryString}${hash ? "#" + hash : ""}`; + window.parent.postMessage( + { + type: "urlUpdate", + path: fullPath, + }, + "https://huggingface.co" + ); + } catch (e) { + console.warn("Unable to sync URL with parent:", e); + } + } + }; + + const linkStyle = (isActive = false) => ({ + textDecoration: "none", + color: isActive ? "text.primary" : "text.secondary", + fontSize: "0.8125rem", + opacity: isActive ? 1 : 0.8, + display: "flex", + alignItems: "center", + gap: 0.5, + paddingBottom: "2px", + cursor: "pointer", + position: "relative", + "&:hover": { + opacity: 1, + color: "text.primary", + }, + "&::after": isActive + ? { + content: '""', + position: "absolute", + bottom: "-4px", + left: "0", + width: "100%", + height: "2px", + backgroundColor: (theme) => + alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.3 : 0.2 + ), + borderRadius: "2px", + } + : {}, + }); + + const Separator = () => ( + ({ + width: "4px", + height: "4px", + borderRadius: "100%", + backgroundColor: alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.2 : 0.15 + ), + })} + /> + ); + + const handleNavigation = (path) => (e) => { + e.preventDefault(); + const searchString = searchParams.toString(); + const queryString = searchString ? `?${searchString}` : ""; + const newPath = `${path}${queryString}`; + + // Local navigation via React Router + navigate(newPath); + + // If in HF Space, sync with parent + if (window.location !== window.parent.location) { + syncUrlWithParent(queryString, newPath); + } + }; + + const handleMenuOpen = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + }; + + return ( + + + {isMobile ? ( + + + + + + + `1px solid ${alpha(theme.palette.divider, 0.1)}`, + backgroundColor: (theme) => + theme.palette.mode === "dark" + ? alpha(theme.palette.background.paper, 0.8) + : theme.palette.background.paper, + backdropFilter: "blur(20px)", + "& .MuiList-root": { + py: 1, + }, + "& .MuiMenuItem-root": { + px: 2, + py: 1, + fontSize: "0.8125rem", + color: "text.secondary", + transition: "all 0.2s ease-in-out", + position: "relative", + "&:hover": { + backgroundColor: (theme) => + alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.1 : 0.06 + ), + color: "text.primary", + }, + "&.Mui-selected": { + backgroundColor: "transparent", + color: "text.primary", + "&::after": { + content: '""', + position: "absolute", + left: "8px", + width: "4px", + height: "100%", + top: "0", + backgroundColor: (theme) => + alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.3 : 0.2 + ), + borderRadius: "2px", + }, + "&:hover": { + backgroundColor: (theme) => + alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.1 : 0.06 + ), + }, + }, + }, + }, + }} + transformOrigin={{ horizontal: "left", vertical: "top" }} + anchorOrigin={{ horizontal: "left", vertical: "bottom" }} + > + {/* Navigation Section */} + + + Navigation + + + { + handleNavigation("/")(e); + handleMenuClose(); + }} + selected={location.pathname === "/"} + > + Leaderboard + + { + handleNavigation("/add")(e); + handleMenuClose(); + }} + selected={location.pathname === "/add"} + > + Submit model + + { + handleNavigation("/quote")(e); + handleMenuClose(); + }} + selected={location.pathname === "/quote"} + > + Citations + + + {/* Separator */} + + `1px solid ${alpha(theme.palette.divider, 0.1)}`, + }} + /> + + {/* External Links Section */} + + + External links + + + + + + ({ + color: "text.secondary", + borderRadius: "100%", + padding: 0, + width: "36px", + height: "36px", + display: "flex", + alignItems: "center", + justifyContent: "center", + transition: "all 0.2s ease-in-out", + "&:hover": { + color: "text.primary", + backgroundColor: alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.1 : 0.06 + ), + }, + "&.MuiButtonBase-root": { + overflow: "hidden", + }, + "& .MuiTouchRipple-root": { + color: alpha(theme.palette.text.primary, 0.3), + }, + })} + > + {mode === "light" ? ( + + ) : ( + + )} + + + + ) : ( + // Desktop version + + {/* Internal navigation */} + + + Leaderboard + + + Submit model + + + Citations + + + + + + + {/* Dark mode toggle */} + + ({ + color: "text.secondary", + borderRadius: "100%", + padding: 0, + width: "36px", + height: "36px", + display: "flex", + alignItems: "center", + justifyContent: "center", + transition: "all 0.2s ease-in-out", + "&:hover": { + color: "text.primary", + backgroundColor: alpha( + theme.palette.text.primary, + theme.palette.mode === "dark" ? 0.1 : 0.06 + ), + }, + "&.MuiButtonBase-root": { + overflow: "hidden", + }, + "& .MuiTouchRipple-root": { + color: alpha(theme.palette.text.primary, 0.3), + }, + })} + > + {mode === "light" ? ( + + ) : ( + + )} + + + + )} + + + ); +}; + +export default Navigation; diff --git a/frontend/src/components/shared/AuthContainer.js b/frontend/src/components/shared/AuthContainer.js new file mode 100644 index 0000000000000000000000000000000000000000..7b4955845c03b07e6918827808723186997938b4 --- /dev/null +++ b/frontend/src/components/shared/AuthContainer.js @@ -0,0 +1,139 @@ +import React from "react"; +import { + Box, + Typography, + Button, + Chip, + Stack, + Paper, + CircularProgress, +} from "@mui/material"; +import HFLogo from "../Logo/HFLogo"; +import { useAuth } from "../../hooks/useAuth"; +import LogoutIcon from "@mui/icons-material/Logout"; +import { useNavigate } from "react-router-dom"; + +function AuthContainer({ actionText = "DO_ACTION" }) { + const { isAuthenticated, user, login, logout, loading } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = () => { + if (isAuthenticated && logout) { + logout(); + navigate("/", { replace: true }); + window.location.reload(); + } + }; + + if (loading) { + return ( + + + + ); + } + + if (!isAuthenticated) { + return ( + + + Login to {actionText} + + + You need to be logged in with your Hugging Face account to{" "} + {actionText.toLowerCase()} + + + + ); + } + + return ( + + + + + Connected as {user?.username} + + + + + + + ); +} + +export default AuthContainer; diff --git a/frontend/src/components/shared/CodeBlock.js b/frontend/src/components/shared/CodeBlock.js new file mode 100644 index 0000000000000000000000000000000000000000..6f06f6eed1f6a17dd70334d3a7bb4d0ab897355c --- /dev/null +++ b/frontend/src/components/shared/CodeBlock.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { Box, IconButton } from '@mui/material'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; + +const CodeBlock = ({ code }) => ( + + navigator.clipboard.writeText(code)} + sx={{ + position: 'absolute', + top: 8, + right: 8, + color: 'grey.500', + '&:hover': { color: 'grey.300' }, + }} + > + + + + {code} + + +); + +export default CodeBlock; \ No newline at end of file diff --git a/frontend/src/components/shared/FilterTag.js b/frontend/src/components/shared/FilterTag.js new file mode 100644 index 0000000000000000000000000000000000000000..3cd154cb61a699bf94a2af0ba78286e3588aa754 --- /dev/null +++ b/frontend/src/components/shared/FilterTag.js @@ -0,0 +1,139 @@ +import React from "react"; +import { Chip } from "@mui/material"; +import { useTheme } from "@mui/material/styles"; +import { alpha } from "@mui/material/styles"; +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; +import CheckBoxOutlinedIcon from "@mui/icons-material/CheckBoxOutlined"; + +const FilterTag = ({ + label, + checked, + onChange, + count, + isHideFilter = false, + totalCount = 0, + variant = "tag", + showCheckbox = false, + stacked = false, + sx = {}, +}) => { + const theme = useTheme(); + + const formatCount = (count) => { + if (count === undefined) return ""; + return `${count}`; + }; + + const mainLabel = label; + const countLabel = count !== undefined ? formatCount(count) : ""; + + return ( + + ) : ( + + ) + ) : null + } + label={ + + {mainLabel} + {countLabel && ( + <> + + {countLabel} + + )} + + } + onClick={onChange} + variant="outlined" + color={ + checked + ? variant === "secondary" + ? "secondary" + : "primary" + : "default" + } + size="small" + data-checked={checked} + sx={{ + height: "32px", + fontWeight: 600, + opacity: checked ? 1 : 0.8, + borderRadius: "5px", + borderWidth: "1px", + borderStyle: "solid", + cursor: "pointer", + pl: showCheckbox ? 0.5 : 0, + mr: 0.5, + mb: 0.5, + transition: "opacity 0.2s ease, border-color 0.2s ease", + "& .MuiChip-label": { + px: 0.75, + pl: showCheckbox ? 0.6 : 0.75, + }, + "& .MuiChip-icon": { + mr: 0.5, + pl: 0.2, + }, + "&:hover": { + opacity: 1, + backgroundColor: checked + ? alpha( + theme.palette[variant === "secondary" ? "secondary" : "primary"] + .main, + theme.palette.mode === "light" ? 0.08 : 0.16 + ) + : "action.hover", + borderWidth: "1px", + }, + backgroundColor: checked + ? alpha( + theme.palette[variant === "secondary" ? "secondary" : "primary"] + .main, + theme.palette.mode === "light" ? 0.08 : 0.16 + ) + : "background.paper", + borderColor: checked + ? variant === "secondary" + ? "secondary.main" + : "primary.main" + : "divider", + ...sx, + }} + /> + ); +}; + +export default FilterTag; diff --git a/frontend/src/components/shared/InfoIconWithTooltip.js b/frontend/src/components/shared/InfoIconWithTooltip.js new file mode 100644 index 0000000000000000000000000000000000000000..2b307ccaf8d7bebb91c81b2ff7cf746a4fbac05e --- /dev/null +++ b/frontend/src/components/shared/InfoIconWithTooltip.js @@ -0,0 +1,87 @@ +import React from "react"; +import { Box, Tooltip, Portal, Backdrop } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; + +const InfoIconWithTooltip = ({ tooltip, iconProps = {}, sx = {} }) => { + const [open, setOpen] = React.useState(false); + + return ( + <> + setOpen(true)} + onClose={() => setOpen(false)} + componentsProps={{ + tooltip: { + sx: { + bgcolor: "rgba(33, 33, 33, 0.95)", + padding: "12px 16px", + maxWidth: "none !important", + width: "auto", + minWidth: "200px", + fontSize: "0.875rem", + lineHeight: 1.5, + position: "relative", + zIndex: 1501, + "& .MuiTooltip-arrow": { + color: "rgba(33, 33, 33, 0.95)", + }, + }, + }, + popper: { + sx: { + zIndex: 1501, + maxWidth: "min(600px, 90vw) !important", + '&[data-popper-placement*="bottom"] .MuiTooltip-tooltip': { + marginTop: "10px", + }, + '&[data-popper-placement*="top"] .MuiTooltip-tooltip': { + marginBottom: "10px", + }, + }, + }, + }} + > + + + + + {open && ( + + + + )} + + ); +}; + +export default InfoIconWithTooltip; diff --git a/frontend/src/components/shared/PageHeader.js b/frontend/src/components/shared/PageHeader.js new file mode 100644 index 0000000000000000000000000000000000000000..5aada38223f216bc7df8c65725dda552947e757d --- /dev/null +++ b/frontend/src/components/shared/PageHeader.js @@ -0,0 +1,29 @@ +import React from "react"; +import { Box, Typography } from "@mui/material"; + +const PageHeader = ({ title, subtitle }) => { + return ( + + + {title} + + {subtitle && ( + + {subtitle} + + )} + + ); +}; + +export default PageHeader; diff --git a/frontend/src/config/auth.js b/frontend/src/config/auth.js new file mode 100644 index 0000000000000000000000000000000000000000..b8a8f6370f0ae2f49fef4f3c5a9632160feb2061 --- /dev/null +++ b/frontend/src/config/auth.js @@ -0,0 +1,7 @@ +export const HF_CONFIG = { + CLIENT_ID: "143c924d-5fa8-47b7-8ea9-d22b5a2c9212", + STORAGE_KEY: "hf_oauth", + SCOPE: "openid profile", + PROD_URL: "https://thefinai-open-greek-finance-llm-leaderboard.hf.space", + DEV_URL: "http://localhost:7860" +}; diff --git a/frontend/src/config/theme.js b/frontend/src/config/theme.js new file mode 100644 index 0000000000000000000000000000000000000000..4bd6e4ae0ac0810a89f7aafb480b3b12fbe0f524 --- /dev/null +++ b/frontend/src/config/theme.js @@ -0,0 +1,390 @@ +import { createTheme, alpha } from "@mui/material/styles"; + +const getDesignTokens = (mode) => ({ + typography: { + fontFamily: [ + "-apple-system", + "BlinkMacSystemFont", + '"Segoe UI"', + "Roboto", + '"Helvetica Neue"', + "Arial", + "sans-serif", + ].join(","), + h1: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + h2: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + h3: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + h4: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + h5: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + h6: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + subtitle1: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + subtitle2: { + fontFamily: '"Source Sans Pro", sans-serif', + }, + }, + palette: { + mode, + primary: { + main: "#4F86C6", + light: mode === "light" ? "#7BA7D7" : "#6B97D7", + dark: mode === "light" ? "#2B5C94" : "#3B6CA4", + 50: mode === "light" ? alpha("#4F86C6", 0.05) : alpha("#4F86C6", 0.15), + 100: mode === "light" ? alpha("#4F86C6", 0.1) : alpha("#4F86C6", 0.2), + 200: mode === "light" ? alpha("#4F86C6", 0.2) : alpha("#4F86C6", 0.3), + contrastText: "#fff", + }, + background: { + default: mode === "light" ? "#f8f9fa" : "#0a0a0a", + paper: mode === "light" ? "#fff" : "#1a1a1a", + subtle: mode === "light" ? "grey.100" : "grey.900", + hover: mode === "light" ? "action.hover" : alpha("#fff", 0.08), + tooltip: mode === "light" ? alpha("#212121", 0.9) : alpha("#fff", 0.9), + }, + text: { + primary: mode === "light" ? "rgba(0, 0, 0, 0.87)" : "#fff", + secondary: + mode === "light" ? "rgba(0, 0, 0, 0.6)" : "rgba(255, 255, 255, 0.7)", + disabled: + mode === "light" ? "rgba(0, 0, 0, 0.38)" : "rgba(255, 255, 255, 0.5)", + hint: + mode === "light" ? "rgba(0, 0, 0, 0.38)" : "rgba(255, 255, 255, 0.5)", + }, + divider: + mode === "light" ? "rgba(0, 0, 0, 0.12)" : "rgba(255, 255, 255, 0.12)", + action: { + active: + mode === "light" ? "rgba(0, 0, 0, 0.54)" : "rgba(255, 255, 255, 0.7)", + hover: + mode === "light" ? "rgba(0, 0, 0, 0.04)" : "rgba(255, 255, 255, 0.08)", + selected: + mode === "light" ? "rgba(0, 0, 0, 0.08)" : "rgba(255, 255, 255, 0.16)", + disabled: + mode === "light" ? "rgba(0, 0, 0, 0.26)" : "rgba(255, 255, 255, 0.3)", + disabledBackground: + mode === "light" ? "rgba(0, 0, 0, 0.12)" : "rgba(255, 255, 255, 0.12)", + }, + }, + shape: { + borderRadius: 8, + }, + components: { + MuiCssBaseline: { + styleOverrides: { + "html, body": { + backgroundColor: "background.default", + color: mode === "dark" ? "#fff" : "#000", + }, + body: { + "& *::-webkit-scrollbar": { + width: 8, + height: 8, + backgroundColor: "transparent", + }, + "& *::-webkit-scrollbar-thumb": { + borderRadius: 8, + backgroundColor: + mode === "light" ? alpha("#000", 0.2) : alpha("#fff", 0.1), + "&:hover": { + backgroundColor: + mode === "light" ? alpha("#000", 0.3) : alpha("#fff", 0.15), + }, + }, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + borderRadius: 8, + }, + }, + }, + MuiPaper: { + defaultProps: { + elevation: 0, + }, + styleOverrides: { + root: { + backgroundImage: "none", + boxShadow: "none", + border: "1px solid", + borderColor: + mode === "light" + ? "rgba(0, 0, 0, 0.12)!important" + : "rgba(255, 255, 255, 0.25)!important", + }, + rounded: { + borderRadius: 12, + }, + }, + }, + + MuiTableCell: { + styleOverrides: { + root: { + borderColor: (theme) => + alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + ), + }, + head: { + backgroundColor: mode === "light" ? "grey.50" : "grey.900", + color: "text.primary", + fontWeight: 600, + }, + }, + }, + MuiTableRow: { + styleOverrides: { + root: { + backgroundColor: "transparent", + }, + }, + }, + MuiTableContainer: { + styleOverrides: { + root: { + backgroundColor: "background.paper", + borderRadius: 8, + border: "none", + boxShadow: "none", + }, + }, + }, + MuiSlider: { + styleOverrides: { + root: { + "& .MuiSlider-valueLabel": { + backgroundColor: "background.paper", + color: "text.primary", + border: "1px solid", + borderColor: "divider", + boxShadow: + mode === "light" + ? "0px 2px 4px rgba(0, 0, 0, 0.1)" + : "0px 2px 4px rgba(0, 0, 0, 0.3)", + }, + }, + thumb: { + "&:hover": { + boxShadow: (theme) => + `0px 0px 0px 8px ${alpha( + theme.palette.primary.main, + mode === "light" ? 0.08 : 0.16 + )}`, + }, + "&.Mui-active": { + boxShadow: (theme) => + `0px 0px 0px 12px ${alpha( + theme.palette.primary.main, + mode === "light" ? 0.08 : 0.16 + )}`, + }, + }, + track: { + border: "none", + }, + rail: { + opacity: mode === "light" ? 0.38 : 0.3, + }, + mark: { + backgroundColor: mode === "light" ? "grey.400" : "grey.600", + }, + markLabel: { + color: "text.secondary", + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + "& .MuiOutlinedInput-root": { + borderRadius: 8, + }, + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + borderRadius: 8, + }, + outlinedInfo: { + borderWidth: 2, + fontWeight: 600, + bgcolor: "info.100", + borderColor: "info.400", + color: "info.700", + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: "info.200", + }, + }, + outlinedWarning: { + borderWidth: 2, + fontWeight: 600, + bgcolor: "warning.100", + borderColor: "warning.400", + color: "warning.700", + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: "warning.200", + }, + }, + outlinedSuccess: { + borderWidth: 2, + fontWeight: 600, + bgcolor: "success.100", + borderColor: "success.400", + color: "success.700", + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: "success.200", + }, + }, + outlinedError: { + borderWidth: 2, + fontWeight: 600, + bgcolor: "error.100", + borderColor: "error.400", + color: "error.700", + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: "error.200", + }, + }, + outlinedPrimary: { + borderWidth: 2, + fontWeight: 600, + bgcolor: "primary.100", + borderColor: "primary.400", + color: "primary.700", + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: "primary.200", + }, + }, + outlinedSecondary: { + borderWidth: 2, + fontWeight: 600, + bgcolor: "secondary.100", + borderColor: "secondary.400", + color: "secondary.700", + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: "secondary.200", + }, + }, + }, + }, + MuiIconButton: { + styleOverrides: { + root: { + borderRadius: 8, + padding: "8px", + "&.MuiIconButton-sizeSmall": { + padding: "4px", + borderRadius: 6, + }, + }, + }, + }, + MuiTooltip: { + styleOverrides: { + tooltip: { + backgroundColor: + mode === "light" ? alpha("#212121", 0.9) : alpha("#424242", 0.9), + color: "#fff", + fontSize: "0.875rem", + padding: "8px 12px", + maxWidth: 400, + borderRadius: 8, + lineHeight: 1.4, + border: "1px solid", + borderColor: + mode === "light" ? alpha("#fff", 0.1) : alpha("#fff", 0.05), + boxShadow: + mode === "light" + ? "0 2px 8px rgba(0, 0, 0, 0.15)" + : "0 2px 8px rgba(0, 0, 0, 0.5)", + "& b": { + fontWeight: 600, + color: "inherit", + }, + "& a": { + color: mode === "light" ? "#90caf9" : "#64b5f6", + textDecoration: "none", + "&:hover": { + textDecoration: "underline", + }, + }, + }, + arrow: { + color: + mode === "light" ? alpha("#212121", 0.9) : alpha("#424242", 0.9), + "&:before": { + border: "1px solid", + borderColor: + mode === "light" ? alpha("#fff", 0.1) : alpha("#fff", 0.05), + }, + }, + }, + defaultProps: { + arrow: true, + enterDelay: 400, + leaveDelay: 200, + }, + }, + MuiAppBar: { + styleOverrides: { + root: { + border: "none", + borderBottom: "none", + }, + }, + }, + }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 900, + lg: 1240, + xl: 1536, + }, + }, +}); + +const getTheme = (mode) => { + const tokens = getDesignTokens(mode); + return createTheme(tokens); +}; + +export default getTheme; diff --git a/frontend/src/hooks/useAuth.js b/frontend/src/hooks/useAuth.js new file mode 100644 index 0000000000000000000000000000000000000000..166d61aaaea425b8ec6e0c1d6bcf16311a94f369 --- /dev/null +++ b/frontend/src/hooks/useAuth.js @@ -0,0 +1,173 @@ +import { useState, useEffect } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub"; +import { HF_CONFIG } from "../config/auth"; + +async function fetchUserInfo(token) { + const response = await fetch("https://huggingface.co/api/whoami-v2", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (!response.ok) { + throw new Error("Failed to fetch user info"); + } + return response.json(); +} + +export function useAuth() { + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const location = useLocation(); + const navigate = useNavigate(); + + // Initialisation de l'authentification + useEffect(() => { + let mounted = true; + const initAuth = async () => { + try { + console.group("Auth Initialization"); + setLoading(true); + + // Vérifier s'il y a une redirection OAuth d'abord + let oauthResult = await oauthHandleRedirectIfPresent(); + + // Si pas de redirection, vérifier le localStorage + if (!oauthResult) { + const storedAuth = localStorage.getItem(HF_CONFIG.STORAGE_KEY); + if (storedAuth) { + try { + oauthResult = JSON.parse(storedAuth); + console.log("Found existing auth"); + const userInfo = await fetchUserInfo(oauthResult.access_token); + if (mounted) { + setIsAuthenticated(true); + setUser({ + username: userInfo.name, + token: oauthResult.access_token, + }); + } + } catch (err) { + console.log("Invalid stored auth data, clearing...", err); + localStorage.removeItem(HF_CONFIG.STORAGE_KEY); + if (mounted) { + setIsAuthenticated(false); + setUser(null); + } + } + } + } else { + console.log("Processing OAuth redirect"); + const token = oauthResult.accessToken; + const userInfo = await fetchUserInfo(token); + + const authData = { + access_token: token, + username: userInfo.name, + }; + + localStorage.setItem(HF_CONFIG.STORAGE_KEY, JSON.stringify(authData)); + + if (mounted) { + setIsAuthenticated(true); + setUser({ + username: userInfo.name, + token: token, + }); + } + + // Rediriger vers la page d'origine + const returnTo = localStorage.getItem("auth_return_to"); + if (returnTo) { + navigate(returnTo); + localStorage.removeItem("auth_return_to"); + } + } + } catch (err) { + console.error("Auth initialization error:", err); + if (mounted) { + setError(err.message); + setIsAuthenticated(false); + setUser(null); + } + } finally { + if (mounted) { + setLoading(false); + } + console.groupEnd(); + } + }; + + initAuth(); + + return () => { + mounted = false; + }; + }, [navigate, location.pathname]); + + const login = async () => { + try { + console.group("Login Process"); + setLoading(true); + + // Sauvegarder la route actuelle pour la redirection post-auth + const currentRoute = window.location.hash.replace("#", "") || "/"; + localStorage.setItem("auth_return_to", currentRoute); + + // Déterminer l'URL de redirection en fonction de l'environnement + const redirectUrl = + window.location.hostname === "localhost" || + window.location.hostname === "127.0.0.1" + ? HF_CONFIG.DEV_URL + : HF_CONFIG.PROD_URL; + + console.log("Using redirect URL:", redirectUrl); + + // Générer l'URL de login et rediriger + const loginUrl = await oauthLoginUrl({ + clientId: HF_CONFIG.CLIENT_ID, + redirectUrl, + scope: HF_CONFIG.SCOPE, + }); + + window.location.href = loginUrl + "&prompt=consent"; + + console.groupEnd(); + } catch (err) { + console.error("Login error:", err); + setError(err.message); + setLoading(false); + console.groupEnd(); + } + }; + + const logout = () => { + console.group("Logout Process"); + setLoading(true); + try { + console.log("Clearing auth data..."); + localStorage.removeItem(HF_CONFIG.STORAGE_KEY); + localStorage.removeItem("auth_return_to"); + setIsAuthenticated(false); + setUser(null); + console.log("Logged out successfully"); + } catch (err) { + console.error("Logout error:", err); + setError(err.message); + } finally { + setLoading(false); + console.groupEnd(); + } + }; + + return { + isAuthenticated, + user, + loading, + error, + login, + logout, + }; +} diff --git a/frontend/src/hooks/useThemeMode.js b/frontend/src/hooks/useThemeMode.js new file mode 100644 index 0000000000000000000000000000000000000000..93030109e2b32281c05178cc4207cb5544e94e4f --- /dev/null +++ b/frontend/src/hooks/useThemeMode.js @@ -0,0 +1,28 @@ +import { useState, useEffect } from 'react'; + +export const useThemeMode = () => { + // Get system preference + const getSystemPreference = () => { + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + }; + + // Initialize theme mode from system preference + const [mode, setMode] = useState(getSystemPreference); + + // Listen to system preference changes + useEffect(() => { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = (e) => { + setMode(e.matches ? 'dark' : 'light'); + }; + + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + }, []); + + const toggleTheme = () => { + setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + }; + + return { mode, toggleTheme }; +}; \ No newline at end of file diff --git a/frontend/src/index.js b/frontend/src/index.js new file mode 100644 index 0000000000000000000000000000000000000000..8db5acb8fb94a08138a3901be0b5b810c9e50931 --- /dev/null +++ b/frontend/src/index.js @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; + +const root = ReactDOM.createRoot(document.getElementById("root")); +root.render( + + + +); diff --git a/frontend/src/pages/AddModelPage/AddModelPage.js b/frontend/src/pages/AddModelPage/AddModelPage.js new file mode 100644 index 0000000000000000000000000000000000000000..5cfb4c9b7af6e2f64b22f7d06cc82c940d981c84 --- /dev/null +++ b/frontend/src/pages/AddModelPage/AddModelPage.js @@ -0,0 +1,48 @@ +import React from "react"; +import { Box, CircularProgress } from "@mui/material"; +import { useAuth } from "../../hooks/useAuth"; +import PageHeader from "../../components/shared/PageHeader"; +import EvaluationQueues from "./components/EvaluationQueues/EvaluationQueues"; +import ModelSubmissionForm from "./components/ModelSubmissionForm/ModelSubmissionForm"; +import SubmissionGuide from "./components/SubmissionGuide/SubmissionGuide"; + +function AddModelPage() { + const { isAuthenticated, loading, user } = useAuth(); + + if (loading) { + return ( + + + + ); + } + + return ( + + + Add your model to the Open + LLM Leaderboard + + } + /> + + + + + + + + ); +} + +export default AddModelPage; diff --git a/frontend/src/pages/AddModelPage/components/EvaluationQueues/EvaluationQueues.js b/frontend/src/pages/AddModelPage/components/EvaluationQueues/EvaluationQueues.js new file mode 100644 index 0000000000000000000000000000000000000000..2c82c74741d1ce24dbe6e809f90c9da562a1b20b --- /dev/null +++ b/frontend/src/pages/AddModelPage/components/EvaluationQueues/EvaluationQueues.js @@ -0,0 +1,718 @@ +import React, { useState, useEffect, useRef } from "react"; +import { + Box, + Typography, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Chip, + Link, + CircularProgress, + Alert, + Accordion, + AccordionSummary, + AccordionDetails, + Stack, + Tooltip, +} from "@mui/material"; +import AccessTimeIcon from "@mui/icons-material/AccessTime"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import PendingIcon from "@mui/icons-material/Pending"; +import AutorenewIcon from "@mui/icons-material/Autorenew"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; +import { useVirtualizer } from "@tanstack/react-virtual"; + +// Function to format wait time +const formatWaitTime = (waitTimeStr) => { + const seconds = parseFloat(waitTimeStr.replace("s", "")); + + if (seconds < 60) { + return "just now"; + } + + const minutes = Math.floor(seconds / 60); + if (minutes < 60) { + return `${minutes}m ago`; + } + + const hours = Math.floor(minutes / 60); + if (hours < 24) { + return `${hours}h ago`; + } + + const days = Math.floor(hours / 24); + return `${days}d ago`; +}; + +// Column definitions with their properties +const columns = [ + { + id: "model", + label: "Model", + width: "35%", + align: "left", + }, + { + id: "submitter", + label: "Submitted by", + width: "15%", + align: "left", + }, + { + id: "wait_time", + label: "Submitted", + width: "12%", + align: "center", + }, + { + id: "precision", + label: "Precision", + width: "13%", + align: "center", + }, + { + id: "revision", + label: "Revision", + width: "12%", + align: "center", + }, + { + id: "status", + label: "Status", + width: "13%", + align: "center", + }, +]; + +const StatusChip = ({ status }) => { + const statusConfig = { + finished: { + icon: , + label: "Completed", + color: "success", + }, + evaluating: { + icon: , + label: "Evaluating", + color: "warning", + }, + pending: { icon: , label: "Pending", color: "info" }, + }; + + const config = statusConfig[status] || statusConfig.pending; + + return ( + + ); +}; + +const ModelTable = ({ models, emptyMessage, status }) => { + const parentRef = useRef(null); + const rowVirtualizer = useVirtualizer({ + count: models.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 53, + overscan: 5, + }); + + if (models.length === 0) { + return ( + + {emptyMessage} + + ); + } + + return ( + + + + {columns.map((column) => ( + + ))} + + + + {columns.map((column, index) => ( + + {column.label} + + ))} + + + + + +
+ {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const model = models[virtualRow.index]; + const waitTime = formatWaitTime(model.wait_time); + + return ( + + + + {model.name} + + + + + {model.submitter} + + + + + + {waitTime} + + + + + + {model.precision} + + + + {model.revision.substring(0, 7)} + + + + + + ); + })} +
+
+
+
+
+
+ ); +}; + +const QueueAccordion = ({ + title, + models, + status, + emptyMessage, + expanded, + onChange, + loading, +}) => ( + + }> + + {title} + + ({ + borderWidth: 2, + fontWeight: 600, + bgcolor: + status === "finished" + ? theme.palette.success[100] + : status === "evaluating" + ? theme.palette.warning[100] + : theme.palette.info[100], + borderColor: + status === "finished" + ? theme.palette.success[400] + : status === "evaluating" + ? theme.palette.warning[400] + : theme.palette.info[400], + color: + status === "finished" + ? theme.palette.success[700] + : status === "evaluating" + ? theme.palette.warning[700] + : theme.palette.info[700], + "& .MuiChip-label": { + px: 1.2, + }, + "&:hover": { + bgcolor: + status === "finished" + ? theme.palette.success[200] + : status === "evaluating" + ? theme.palette.warning[200] + : theme.palette.info[200], + }, + })} + /> + {loading && ( + + )} + + + + + + + + + +); + +const EvaluationQueues = ({ defaultExpanded = true }) => { + const [expanded, setExpanded] = useState(defaultExpanded); + const [expandedQueues, setExpandedQueues] = useState(new Set()); + const [models, setModels] = useState({ + pending: [], + evaluating: [], + finished: [], + }); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchModels = async () => { + try { + const response = await fetch("/api/models/status"); + if (!response.ok) { + throw new Error("Failed to fetch models"); + } + const data = await response.json(); + + // Sort models by submission date (most recent first) + const sortByDate = (models) => { + return [...models].sort((a, b) => { + const dateA = new Date(a.submission_time); + const dateB = new Date(b.submission_time); + return dateB - dateA; + }); + }; + + setModels({ + finished: sortByDate(data.finished), + evaluating: sortByDate(data.evaluating), + pending: sortByDate(data.pending), + }); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + fetchModels(); + const interval = setInterval(fetchModels, 30000); + return () => clearInterval(interval); + }, []); + + const handleMainAccordionChange = (panel) => (event, isExpanded) => { + setExpanded(isExpanded ? panel : false); + }; + + const handleQueueAccordionChange = (queueName) => (event, isExpanded) => { + setExpandedQueues((prev) => { + const newSet = new Set(prev); + if (isExpanded) { + newSet.add(queueName); + } else { + newSet.delete(queueName); + } + return newSet; + }); + }; + + if (error) { + return ( + + {error} + + ); + } + + return ( + + } + sx={{ + px: 3, + "& .MuiAccordionSummary-expandIconWrapper": { + color: "text.secondary", + transform: "rotate(0deg)", + transition: "transform 150ms", + "&.Mui-expanded": { + transform: "rotate(180deg)", + }, + }, + }} + > + + + Evaluation Status + + {!loading && ( + + + + + + )} + {loading && ( + + )} + + + + {loading ? ( + + + + ) : ( + <> + + + + + + + )} + + + ); +}; + +export default EvaluationQueues; diff --git a/frontend/src/pages/AddModelPage/components/ModelSubmissionForm/ModelSubmissionForm.js b/frontend/src/pages/AddModelPage/components/ModelSubmissionForm/ModelSubmissionForm.js new file mode 100644 index 0000000000000000000000000000000000000000..3255a9d57a971490c8d815e9d16af8d35424e8bd --- /dev/null +++ b/frontend/src/pages/AddModelPage/components/ModelSubmissionForm/ModelSubmissionForm.js @@ -0,0 +1,599 @@ +import React, { useState } from "react"; +import { + Box, + Paper, + Typography, + TextField, + Button, + FormControl, + InputLabel, + Select, + MenuItem, + FormControlLabel, + Switch, + Stack, + Grid, + CircularProgress, + Alert, +} from "@mui/material"; +import RocketLaunchIcon from "@mui/icons-material/RocketLaunch"; +import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline"; +import ThumbUpIcon from "@mui/icons-material/ThumbUp"; +import { alpha } from "@mui/material/styles"; +import InfoIconWithTooltip from "../../../../components/shared/InfoIconWithTooltip"; +import { MODEL_TYPES } from "../../../../pages/LeaderboardPage/components/Leaderboard/constants/modelTypes"; +import { SUBMISSION_PRECISIONS } from "../../../../pages/LeaderboardPage/components/Leaderboard/constants/defaults"; +import AuthContainer from "../../../../components/shared/AuthContainer"; + +const WEIGHT_TYPES = [ + { value: "Original", label: "Original" }, + { value: "Delta", label: "Delta" }, + { value: "Adapter", label: "Adapter" }, +]; + +const HELP_TEXTS = { + modelName: ( + + + Model Name on Hugging Face Hub + + + Your model must be public and loadable with AutoClasses without + trust_remote_code. The model should be in Safetensors format for better + safety and loading performance. Example: mistralai/Mistral-7B-v0.1 + + + ), + revision: ( + + + Model Revision + + + Git branch, tag or commit hash. The evaluation will be strictly tied to + this specific commit to ensure consistency. Make sure this version is + stable and contains all necessary files. + + + ), + modelType: ( + + + Model Category + + + 🟢 Pretrained: Base models trained on text using masked modeling 🟩 + Continuously Pretrained: Extended training on additional corpus 🔶 + Fine-tuned: Domain-specific optimization 💬 Chat: Models using RLHF, + DPO, or IFT for conversation 🤝 Merge: Combined weights without + additional training 🌸 Multimodal: Handles multiple input types + + + ), + baseModel: ( + + + Base Model Reference + + + Required for delta weights or adapters. This information is used to + identify the original model and calculate the total parameter count by + combining base model and adapter/delta parameters. + + + ), + precision: ( + + + Model Precision + + + Size limits vary by precision: • FP16/BF16: up to 100B parameters • + 8-bit: up to 280B parameters (2x) • 4-bit: up to 560B parameters (4x) + Choose carefully as incorrect precision can cause evaluation errors. + + + ), + weightsType: ( + + + Weights Format + + + Original: Complete model weights in safetensors format Delta: Weight + differences from base model (requires base model for size calculation) + Adapter: Lightweight fine-tuning layers (requires base model for size + calculation) + + + ), + chatTemplate: ( + + + Chat Template Support + + + Activates automatically for chat models. It uses the standardized Hugging + Face chat template for consistent prompt formatting during evaluation. + Required for models using RLHF, DPO, or instruction fine-tuning. + + + ), +}; + +// Convert MODEL_TYPES to format expected by Select component +const modelTypeOptions = Object.entries(MODEL_TYPES).map( + ([value, { icon, label }]) => ({ + value, + label: `${icon} ${label}`, + }) +); + +function ModelSubmissionForm({ user, isAuthenticated }) { + const [formData, setFormData] = useState({ + modelName: "", + revision: "main", + modelType: "fine-tuned", + isChatModel: false, + useChatTemplate: false, + precision: "float16", + weightsType: "Original", + baseModel: "", + }); + const [error, setError] = useState(null); + const [submitting, setSubmitting] = useState(false); + const [success, setSuccess] = useState(false); + const [submittedData, setSubmittedData] = useState(null); + + const handleChange = (event) => { + const { name, value, checked } = event.target; + setFormData((prev) => ({ + ...prev, + [name]: event.target.type === "checkbox" ? checked : value, + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(null); + setSubmitting(true); + + try { + const response = await fetch("/api/models/submit", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model_id: formData.modelName, + revision: formData.revision, + model_type: formData.modelType, + precision: formData.precision, + weight_type: formData.weightsType, + base_model: formData.baseModel, + use_chat_template: formData.useChatTemplate, + user_id: user.username, + }), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail || "Failed to submit model"); + } + + setSubmittedData(formData); + setSuccess(true); + } catch (error) { + setError(error.message); + } finally { + setSubmitting(false); + } + }; + + if (success && submittedData) { + return ( + ({ + p: 6, + mb: 3, + bgcolor: alpha(theme.palette.success.main, 0.05), + borderColor: alpha(theme.palette.success.main, 0.2), + })} + > + + + + + Model submitted successfully! + + + + + Your model {submittedData.modelName} has been added + to the evaluation queue with the following parameters: + + + + + + + Model: + + + {submittedData.modelName} + + + + + Type: + + + {submittedData.modelType} + + + + + Revision: + + + {submittedData.revision} + + + + + Precision: + + + {submittedData.precision} + + + + + Weight type: + + + {submittedData.weightsType} + + + {submittedData.baseModel && ( + + + Base model: + + + {submittedData.baseModel} + + + )} + + + Chat template: + + + {submittedData.useChatTemplate ? "Yes" : "No"} + + + + + + + An automatic upvote has been added to your model to help with + prioritization. + + + + + + + + ); + } + + return ( + <> + {error && ( + + {error} + + )} + + {isAuthenticated && ( + + {/* Header */} + + theme.palette.mode === "dark" + ? alpha(theme.palette.divider, 0.1) + : "grey.200", + bgcolor: (theme) => + theme.palette.mode === "dark" + ? alpha(theme.palette.background.paper, 0.5) + : "grey.50", + }} + > + + Model Submission Form + + + + {/* Form Content */} + + + {/* Model Information */} + + + Model Information + + + + + + + ), + }} + /> + + + + + ), + }} + /> + + + {/* Model Configuration */} + + + Model Configuration + + + + + + Model Type + + + + + + + + } + label="Use Chat Template" + /> + + + + + + + Precision + + + + + + + Weights Type + + + + + {formData.weightsType !== "Original" && ( + + + ), + }} + /> + + )} + + {/* Submit Button */} + + + + All fields marked with * are required + + + + + + + + )} + + ); +} + +export default ModelSubmissionForm; diff --git a/frontend/src/pages/AddModelPage/components/SubmissionGuide/SubmissionGuide.js b/frontend/src/pages/AddModelPage/components/SubmissionGuide/SubmissionGuide.js new file mode 100644 index 0000000000000000000000000000000000000000..c023f0ba51929d7bc854df404d5766bba2d5a8ee --- /dev/null +++ b/frontend/src/pages/AddModelPage/components/SubmissionGuide/SubmissionGuide.js @@ -0,0 +1,274 @@ +import React, { useState, useEffect } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import { Box, Paper, Typography, Button, Stack, Collapse } from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; + +const DocLink = ({ href, children }) => ( + +); + +const StepNumber = ({ number }) => ( + + {number} + +); + +const TUTORIAL_STEPS = [ + { + title: "Model Information", + content: ( + + + Your model should be public on the Hub and follow the{" "} + username/model-id format (e.g. + mistralai/Mistral-7B-v0.1). Specify the revision{" "} + (commit hash or branch) and model type. + + + Model uploading guide + + + ), + }, + { + title: "Technical Details", + content: ( + + + Make sure your model can be loaded locally before + submitting: + + + theme.palette.mode === "dark" ? "grey.50" : "grey.900", + borderRadius: 1, + "& pre": { + m: 0, + p: 0, + fontFamily: "monospace", + fontSize: "0.875rem", + color: (theme) => + theme.palette.mode === "dark" ? "grey.900" : "grey.50", + }, + }} + > +
+            {`from transformers import AutoConfig, AutoModel, AutoTokenizer
+
+config = AutoConfig.from_pretrained("your-username/your-model", revision="main")
+model = AutoModel.from_pretrained("your-username/your-model", revision="main")
+tokenizer = AutoTokenizer.from_pretrained("your-username/your-model", revision="main")`}
+          
+
+ + Transformers documentation + +
+ ), + }, + { + title: "License Requirements", + content: ( + + + A license tag is required.{" "} + Open licenses (Apache, MIT, etc) are strongly + recommended. + + + About model licenses + + + ), + }, + { + title: "Model Card Requirements", + content: ( + + + Your model card must include: architecture,{" "} + training details,{" "} + dataset information, intended use, limitations, and{" "} + performance metrics. + + + Model cards guide + + + ), + }, + { + title: "Final Checklist", + content: ( + + + Ensure your model is public, uses{" "} + safetensors format, has a{" "} + license tag, and loads correctly{" "} + with the provided code. + + + Sharing best practices + + + ), + }, +]; + +function SubmissionGuide() { + const location = useLocation(); + const navigate = useNavigate(); + + // Initialize state directly with URL value + const initialExpanded = !new URLSearchParams(location.search).get("guide"); + const [expanded, setExpanded] = useState(initialExpanded); + + // Sync expanded state with URL changes after initial render + useEffect(() => { + const guideOpen = !new URLSearchParams(location.search).get("guide"); + if (guideOpen !== expanded) { + setExpanded(guideOpen); + } + }, [location.search, expanded]); + + const handleAccordionChange = () => { + const newExpanded = !expanded; + setExpanded(newExpanded); + const params = new URLSearchParams(location.search); + if (newExpanded) { + params.delete("guide"); + } else { + params.set("guide", "closed"); + } + navigate({ search: params.toString() }, { replace: true }); + }; + + return ( + + theme.palette.mode === "dark" ? "grey.800" : "grey.200", + overflow: "hidden", + }} + > + + theme.palette.mode === "dark" ? "grey.900" : "grey.50", + borderBottom: "1px solid", + borderColor: (theme) => + expanded + ? theme.palette.mode === "dark" + ? "grey.800" + : "grey.200" + : "transparent", + }} + > + + Submission Guide + + + + + + + {TUTORIAL_STEPS.map((step, index) => ( + + + + + + {step.title} + + + {step.content} + + {index < TUTORIAL_STEPS.length - 1 && ( + + theme.palette.mode === "dark" ? "grey.800" : "grey.100", + }} + /> + )} + + ))} + + + + + ); +} + +export default SubmissionGuide; diff --git a/frontend/src/pages/LeaderboardPage/LeaderboardPage.js b/frontend/src/pages/LeaderboardPage/LeaderboardPage.js new file mode 100644 index 0000000000000000000000000000000000000000..c1e2855ab293d49b0b938040bcab472c8285e943 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/LeaderboardPage.js @@ -0,0 +1,57 @@ +import { useEffect } from "react"; +import Leaderboard from "./components/Leaderboard/Leaderboard"; +import { Box } from "@mui/material"; +import PageHeader from "../../components/shared/PageHeader"; +import Logo from "../../components/Logo/Logo"; +import { useLeaderboardData } from "../../pages/LeaderboardPage/components/Leaderboard/hooks/useLeaderboardData"; +import { useLeaderboard } from "../../pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext"; + +function LeaderboardPage() { + const { data, isLoading, error } = useLeaderboardData(); + const { actions } = useLeaderboard(); + + useEffect(() => { + if (data) { + actions.setModels(data); + } + actions.setLoading(isLoading); + actions.setError(error); + }, [data, isLoading, error, actions]); + + return ( + + + + + + Benchmark for large language models in {" "} + financial domain {" "} + across multiple languages and modalities + + } + /> + + + ); +} + +export default LeaderboardPage; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/Leaderboard.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/Leaderboard.js new file mode 100644 index 0000000000000000000000000000000000000000..5c41ce7fa5eeeb9b00bc657c174c9653c5d31503 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/Leaderboard.js @@ -0,0 +1,449 @@ +import React, { useMemo, useEffect, useCallback } from "react"; +import { Box, Typography } from "@mui/material"; +import { useSearchParams } from "react-router-dom"; + +import { TABLE_DEFAULTS } from "./constants/defaults"; +import { useLeaderboard } from "./context/LeaderboardContext"; +import { useLeaderboardProcessing } from "./hooks/useLeaderboardData"; +import { useLeaderboardData } from "./hooks/useLeaderboardData"; + +import LeaderboardFilters from "./components/Filters/Filters"; +import LeaderboardTable from "./components/Table/Table"; +import SearchBar, { SearchBarSkeleton } from "./components/Filters/SearchBar"; +import PerformanceMonitor from "./components/PerformanceMonitor"; +import QuickFilters, { + QuickFiltersSkeleton, +} from "./components/Filters/QuickFilters"; + +const FilterAccordion = ({ expanded, quickFilters, advancedFilters }) => { + const advancedFiltersRef = React.useRef(null); + const quickFiltersRef = React.useRef(null); + const [height, setHeight] = React.useState("auto"); + const resizeTimeoutRef = React.useRef(null); + + const updateHeight = React.useCallback(() => { + if (expanded && advancedFiltersRef.current) { + setHeight(`${advancedFiltersRef.current.scrollHeight}px`); + } else if (!expanded && quickFiltersRef.current) { + setHeight(`${quickFiltersRef.current.scrollHeight}px`); + } + }, [expanded]); + + React.useEffect(() => { + // Initial height calculation + const timer = setTimeout(updateHeight, 100); + + // Resize handler with debounce + const handleResize = () => { + if (resizeTimeoutRef.current) { + clearTimeout(resizeTimeoutRef.current); + } + resizeTimeoutRef.current = setTimeout(updateHeight, 150); + }; + + window.addEventListener("resize", handleResize); + + return () => { + clearTimeout(timer); + window.removeEventListener("resize", handleResize); + if (resizeTimeoutRef.current) { + clearTimeout(resizeTimeoutRef.current); + } + }; + }, [updateHeight]); + + // Update height when expanded state changes + React.useEffect(() => { + updateHeight(); + }, [expanded, updateHeight]); + + return ( + + + {quickFilters} + + + {advancedFilters} + + + ); +}; + +const Leaderboard = () => { + const { state, actions } = useLeaderboard(); + const [searchParams, setSearchParams] = useSearchParams(); + const { + data, + isLoading: dataLoading, + error: dataError, + } = useLeaderboardData(); + const { + table, + filteredData, + error: processingError, + } = useLeaderboardProcessing(); + + // Memoize filtered data + const memoizedFilteredData = useMemo(() => filteredData, [filteredData]); + const memoizedTable = useMemo(() => table, [table]); + + // Memoize table options + const hasTableOptionsChanges = useMemo(() => { + return ( + state.display.rowSize !== TABLE_DEFAULTS.ROW_SIZE || + JSON.stringify(state.display.scoreDisplay) !== + JSON.stringify(TABLE_DEFAULTS.SCORE_DISPLAY) || + state.display.averageMode !== TABLE_DEFAULTS.AVERAGE_MODE || + state.display.rankingMode !== TABLE_DEFAULTS.RANKING_MODE + ); + }, [state.display]); + + const hasColumnFilterChanges = useMemo(() => { + return ( + JSON.stringify([...state.display.visibleColumns].sort()) !== + JSON.stringify([...TABLE_DEFAULTS.COLUMNS.DEFAULT_VISIBLE].sort()) + ); + }, [state.display.visibleColumns]); + + // Memoize callbacks + const onToggleFilters = useCallback(() => { + actions.toggleFiltersExpanded(); + }, [actions]); + + const onColumnVisibilityChange = useCallback( + (newVisibility) => { + actions.setDisplayOption( + "visibleColumns", + Object.keys(newVisibility).filter((key) => newVisibility[key]) + ); + }, + [actions] + ); + + const onRowSizeChange = useCallback( + (size) => { + actions.setDisplayOption("rowSize", size); + }, + [actions] + ); + + const onScoreDisplayChange = useCallback( + (display) => { + actions.setDisplayOption("scoreDisplay", display); + }, + [actions] + ); + + const onAverageModeChange = useCallback( + (mode) => { + actions.setDisplayOption("averageMode", mode); + }, + [actions] + ); + + const onRankingModeChange = useCallback( + (mode) => { + actions.setDisplayOption("rankingMode", mode); + }, + [actions] + ); + + const onPrecisionsChange = useCallback( + (precisions) => { + actions.setFilter("precisions", precisions); + }, + [actions] + ); + + const onTypesChange = useCallback( + (types) => { + actions.setFilter("types", types); + }, + [actions] + ); + + const onParamsRangeChange = useCallback( + (range) => { + actions.setFilter("paramsRange", range); + }, + [actions] + ); + + const onBooleanFiltersChange = useCallback( + (filters) => { + actions.setFilter("booleanFilters", filters); + }, + [actions] + ); + + const onReset = useCallback(() => { + actions.resetFilters(); + }, [actions]); + + // Memoize loading states + const loadingStates = useMemo(() => { + const isInitialLoading = dataLoading || !data; + const isProcessingData = !memoizedTable || !memoizedFilteredData; + const isApplyingFilters = state.models.length > 0 && !memoizedFilteredData; + const hasValidFilterCounts = + state.countsReady && + state.filterCounts && + state.filterCounts.normal && + state.filterCounts.officialOnly; + + return { + isInitialLoading, + isProcessingData, + isApplyingFilters, + showSearchSkeleton: isInitialLoading || !hasValidFilterCounts, + showFiltersSkeleton: isInitialLoading || !hasValidFilterCounts, + showTableSkeleton: + isInitialLoading || + isProcessingData || + isApplyingFilters || + !hasValidFilterCounts, + }; + }, [ + dataLoading, + data, + memoizedTable, + memoizedFilteredData, + state.models.length, + state.filterCounts, + state.countsReady, + ]); + + // Memoize child components + const memoizedSearchBar = useMemo( + () => ( + + ), + [ + onToggleFilters, + state.filtersExpanded, + loadingStates.showTableSkeleton, + memoizedFilteredData, + table, + ] + ); + + const memoizedQuickFilters = useMemo( + () => ( + + ), + [state.models.length, memoizedFilteredData, memoizedTable] + ); + + const memoizedLeaderboardFilters = useMemo( + () => ( + + ), + [ + memoizedFilteredData, + loadingStates.showFiltersSkeleton, + state.filters.precisions, + state.filters.types, + state.filters.paramsRange, + state.filters.booleanFilters, + onPrecisionsChange, + onTypesChange, + onParamsRangeChange, + onBooleanFiltersChange, + onReset, + ] + ); + + // No need to memoize LeaderboardTable as it handles its own sorting state + const tableComponent = ( + + ); + + // Update context with loaded data + useEffect(() => { + if (data) { + actions.setModels(data); + } + }, [data, actions]); + + // Log to understand loading state + useEffect(() => { + if (process.env.NODE_ENV === "development") { + console.log("Loading state:", { + dataLoading, + hasData: !!data, + hasTable: !!table, + hasFilteredData: !!filteredData, + filteredDataLength: filteredData?.length, + stateModelsLength: state.models.length, + hasFilters: Object.keys(state.filters).some((key) => { + if (Array.isArray(state.filters[key])) { + return state.filters[key].length > 0; + } + return !!state.filters[key]; + }), + }); + } + }, [ + dataLoading, + data, + table, + filteredData?.length, + state.models.length, + filteredData, + state.filters, + ]); + + // If an error occurred, display it + if (dataError || processingError) { + return ( + + + {(dataError || processingError)?.message || + "An error occurred while loading the data"} + + + ); + } + + return ( + + + + + {loadingStates.showSearchSkeleton ? ( + + ) : ( + memoizedSearchBar + )} + + {loadingStates.showFiltersSkeleton ? ( + + ) : ( + + )} + + + + + + {tableComponent} + + + + + ); +}; + +export default Leaderboard; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/ColumnSelector/ColumnSelector.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/ColumnSelector/ColumnSelector.js new file mode 100644 index 0000000000000000000000000000000000000000..5a67cacd3d1d3343d22abcf7fd083440bcb94881 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/ColumnSelector/ColumnSelector.js @@ -0,0 +1,217 @@ +import React from "react"; +import { Box, Typography } from "@mui/material"; +import ViewColumnIcon from "@mui/icons-material/ViewColumn"; +import CloseIcon from "@mui/icons-material/Close"; +import FilterTag from "../../../../../../components/shared/FilterTag"; +import RestartAltIcon from "@mui/icons-material/RestartAlt"; +import { TABLE_DEFAULTS } from "../../constants/defaults"; +import DropdownButton from "../shared/DropdownButton"; +import InfoIconWithTooltip from "../../../../../../components/shared/InfoIconWithTooltip"; +import { UI_TOOLTIPS } from "../../constants/tooltips"; + +const FilterGroup = ({ title, children, count, total }) => ( + + + {title} + {count !== undefined && total !== undefined && ( + + ({count}/{total}) + + )} + + + {children} + + +); + +const ColumnSelector = ({ + table, + onReset, + hasChanges, + onColumnVisibilityChange, + loading = false, +}) => { + const { getState, setColumnVisibility } = table; + const { columnVisibility } = getState(); + + // Filter columns to only show filterable ones + const filterableColumns = [ + ...TABLE_DEFAULTS.COLUMNS.EVALUATION, + ...TABLE_DEFAULTS.COLUMNS.OPTIONAL, + ]; + + const handleReset = (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (!hasChanges) return; + + // Call onReset first + onReset?.(); + + // Create object with all columns set to false by default + const defaultVisibility = {}; + + // Set to true all columns that should be visible by default + TABLE_DEFAULTS.COLUMNS.DEFAULT_VISIBLE.forEach((col) => { + defaultVisibility[col] = true; + }); + + onColumnVisibilityChange?.(defaultVisibility); + setColumnVisibility(defaultVisibility); + }; + + const toggleColumn = (columnId) => { + if (TABLE_DEFAULTS.COLUMNS.FIXED.includes(columnId)) return; + + const newVisibility = { + ...columnVisibility, + [columnId]: !columnVisibility[columnId], + }; + + setColumnVisibility(newVisibility); + onColumnVisibilityChange?.(newVisibility); + }; + + return ( + + + + + Column Visibility + + + + + + + Reset + + + + + {Object.entries(TABLE_DEFAULTS.COLUMNS.COLUMN_GROUPS).map( + ([groupTitle, columns]) => { + // Calculer le nombre de colonnes cochées pour les évaluations + const isEvalGroup = groupTitle === "Evaluation Scores"; + const filteredColumns = columns.filter((col) => + filterableColumns.includes(col) + ); + const checkedCount = isEvalGroup + ? filteredColumns.filter((col) => columnVisibility[col]).length + : undefined; + const totalCount = isEvalGroup ? filteredColumns.length : undefined; + + return ( + + {filteredColumns.map((columnName) => { + const isFixed = + TABLE_DEFAULTS.COLUMNS.FIXED.includes(columnName); + return ( + toggleColumn(columnName)} + disabled={isFixed} + variant="tag" + /> + ); + })} + + ); + } + )} + + ); +}; + +export default ColumnSelector; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/DisplayOptions/DisplayOptions.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/DisplayOptions/DisplayOptions.js new file mode 100644 index 0000000000000000000000000000000000000000..8ec6c2bf0b68a6f2372d867a5a6487128956fb4c --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/DisplayOptions/DisplayOptions.js @@ -0,0 +1,238 @@ +import React from "react"; +import { Box, Typography } from "@mui/material"; +import TuneIcon from "@mui/icons-material/Tune"; +import CloseIcon from "@mui/icons-material/Close"; +import RestartAltIcon from "@mui/icons-material/RestartAlt"; +import FilterTag from "../../../../../../components/shared/FilterTag"; +import { + TABLE_DEFAULTS, + ROW_SIZES, + SCORE_DISPLAY_OPTIONS, + RANKING_MODE_OPTIONS, +} from "../../constants/defaults"; +import { UI_TOOLTIPS } from "../../constants/tooltips"; +import DropdownButton from "../shared/DropdownButton"; +import InfoIconWithTooltip from "../../../../../../components/shared/InfoIconWithTooltip"; + +const TableOptions = ({ + rowSize, + onRowSizeChange, + scoreDisplay = "normalized", + onScoreDisplayChange, + averageMode = "all", + onAverageModeChange, + rankingMode = "static", + onRankingModeChange, + hasChanges, + searchParams, + setSearchParams, + loading = false, +}) => { + const handleReset = () => { + onRowSizeChange(TABLE_DEFAULTS.ROW_SIZE); + onScoreDisplayChange(TABLE_DEFAULTS.SCORE_DISPLAY); + onAverageModeChange(TABLE_DEFAULTS.AVERAGE_MODE); + onRankingModeChange(TABLE_DEFAULTS.RANKING_MODE); + + const newParams = new URLSearchParams(searchParams); + ["rowSize", "scoreDisplay", "averageMode", "rankingMode"].forEach( + (param) => { + newParams.delete(param); + } + ); + setSearchParams(newParams); + }; + + return ( + + + + + Table Options + + + + + + + Reset + + + + + + + + + + {UI_TOOLTIPS.ROW_SIZE.title} + + + + + {Object.keys(ROW_SIZES).map((size) => ( + onRowSizeChange(size)} + variant="tag" + /> + ))} + + + + + + + {UI_TOOLTIPS.SCORE_DISPLAY.title} + + + + + {SCORE_DISPLAY_OPTIONS.map(({ value, label }) => ( + onScoreDisplayChange(value)} + variant="tag" + /> + ))} + + + + + + + {UI_TOOLTIPS.RANKING_MODE.title} + + + + + {RANKING_MODE_OPTIONS.map(({ value, label }) => ( + onRankingModeChange(value)} + variant="tag" + /> + ))} + + + + + + + {UI_TOOLTIPS.AVERAGE_SCORE.title} + + + + + onAverageModeChange("all")} + variant="tag" + /> + onAverageModeChange("visible")} + variant="tag" + /> + + + + + + ); +}; + +export default TableOptions; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/FilteredModelCount.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/FilteredModelCount.js new file mode 100644 index 0000000000000000000000000000000000000000..b066d10e5a069b8d3ef681e41199e00d6d8acc5d --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/FilteredModelCount.js @@ -0,0 +1,246 @@ +import React from "react"; +import { Box, Typography, Skeleton } from "@mui/material"; +import { useMemo } from "react"; +import { useLeaderboard } from "../../context/LeaderboardContext"; + +const useModelCount = ({ totalCount, filteredCount, data, table, loading }) => { + const { state } = useLeaderboard(); + const isOfficialProviderActive = state.filters.isOfficialProviderActive; + const { officialOnly: officialOnlyCounts } = state.filterCounts; + + return useMemo(() => { + if (loading) { + return { + displayCount: 0, + currentFilteredCount: 0, + totalPinnedCount: 0, + filteredPinnedCount: 0, + isOfficialProviderActive, + }; + } + const displayCount = isOfficialProviderActive + ? officialOnlyCounts.maintainersHighlight + : totalCount; + + // Calculate total number of pinned models + const totalPinnedCount = + data?.filter((model) => model.isPinned)?.length || 0; + + // Get current filter criteria + const filterConfig = { + selectedPrecisions: state.filters.precisions, + selectedTypes: state.filters.types, + paramsRange: state.filters.paramsRange, + searchValue: state.filters.search, + selectedBooleanFilters: state.filters.booleanFilters, + isOfficialProviderActive: state.filters.isOfficialProviderActive, + }; + + // Check each pinned model if it would pass filters without its pinned status + const filteredPinnedCount = + data?.filter((model) => { + if (!model.isPinned) return false; + + // Check each filter criteria + + // Filter by official providers + if (filterConfig.isOfficialProviderActive) { + if ( + !model.features?.is_highlighted_by_maintainer && + !model.metadata?.is_highlighted_by_maintainer + ) { + return false; + } + } + + // Filter by precision + if (filterConfig.selectedPrecisions.length > 0) { + if ( + !filterConfig.selectedPrecisions.includes(model.model.precision) + ) { + return false; + } + } + + // Filter by type + if (filterConfig.selectedTypes.length > 0) { + const modelType = model.model.type?.toLowerCase().trim(); + if ( + !filterConfig.selectedTypes.some((type) => + modelType?.includes(type) + ) + ) { + return false; + } + } + + // Filter by parameters + const params = model.metadata.params_billions; + if ( + params < filterConfig.paramsRange[0] || + params >= filterConfig.paramsRange[1] + ) { + return false; + } + + // Filter by search + if (filterConfig.searchValue) { + const searchLower = filterConfig.searchValue.toLowerCase(); + const modelName = model.model.name.toLowerCase(); + if (!modelName.includes(searchLower)) { + return false; + } + } + + // Filter by boolean flags + if (filterConfig.selectedBooleanFilters.length > 0) { + if ( + !filterConfig.selectedBooleanFilters.every((filter) => { + const filterValue = + typeof filter === "object" ? filter.value : filter; + + // Maintainer's Highlight keeps positive logic + if (filterValue === "is_highlighted_by_maintainer") { + return model.features[filterValue]; + } + + // For all other filters, invert the logic + if (filterValue === "is_not_available_on_hub") { + return model.features[filterValue]; + } + + return !model.features[filterValue]; + }) + ) { + return false; + } + } + + // If we get here, the model passes all filters + return true; + })?.length || 0; + + return { + displayCount, + currentFilteredCount: filteredCount, + totalPinnedCount, + filteredPinnedCount, + isOfficialProviderActive, + }; + }, [ + loading, + totalCount, + filteredCount, + data, + state.filters, + isOfficialProviderActive, + officialOnlyCounts.maintainersHighlight, + ]); +}; + +const CountTypography = ({ + value, + color = "text.primary", + loading = false, + pinnedCount = 0, + filteredPinnedCount = 0, + showPinned = false, +}) => { + if (loading) { + return ( + + ); + } + + return ( + + + {value} + + {showPinned && pinnedCount > 0 && ( + + {`+${pinnedCount}`} + + )} + + ); +}; + +const FilteredModelCount = React.memo( + ({ + totalCount = 0, + filteredCount = 0, + hasFilterChanges = false, + loading = false, + data = [], + table = null, + }) => { + const { + displayCount, + currentFilteredCount, + totalPinnedCount, + filteredPinnedCount, + isOfficialProviderActive, + } = useModelCount({ + totalCount, + filteredCount, + data, + table, + loading, + }); + + const shouldHighlight = + !loading && hasFilterChanges && currentFilteredCount !== displayCount; + + // Always show pinned models when they exist + const pinnedToShow = totalPinnedCount; + + return ( + + 0} + /> + + + + ); + } +); + +FilteredModelCount.displayName = "FilteredModelCount"; + +export default FilteredModelCount; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/Filters.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/Filters.js new file mode 100644 index 0000000000000000000000000000000000000000..029aa3a1f6cf89236b0a5d1e5fe8637f18c94be2 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/Filters.js @@ -0,0 +1,850 @@ +import React, { + useState, + useEffect, + useMemo, + useRef, + forwardRef, + useCallback, +} from "react"; +import { + Box, + Typography, + Collapse, + Slider, + Grid, + Accordion, + AccordionDetails, + alpha, + useTheme, + TextField, +} from "@mui/material"; +import { + TABLE_DEFAULTS, + BOOLEAN_FILTER_OPTIONS, + FILTER_PRECISIONS, +} from "../../constants/defaults"; +import FilterTag from "../../../../../../components/shared/FilterTag"; +import { MODEL_TYPE_ORDER, MODEL_TYPES } from "../../constants/modelTypes"; +import { useLeaderboard } from "../../context/LeaderboardContext"; +import InfoIconWithTooltip from "../../../../../../components/shared/InfoIconWithTooltip"; +import { COLUMN_TOOLTIPS } from "../../constants/tooltips"; + +const getTooltipContent = (title) => { + switch (title) { + case "Model Type": + return COLUMN_TOOLTIPS.ARCHITECTURE; + case "Precision format": + return COLUMN_TOOLTIPS.PRECISION; + case "Flags": + return COLUMN_TOOLTIPS.FLAGS; + case "Parameters": + return COLUMN_TOOLTIPS.PARAMETERS; + default: + return null; + } +}; + +const FilterGroup = ({ + title, + tooltip, + children, + paramsRange, + onParamsRangeChange, +}) => { + const theme = useTheme(); + const [localParamsRange, setLocalParamsRange] = useState(paramsRange); + const stableTimerRef = useRef(null); + + // Handle local range change + const handleLocalRangeChange = useCallback((event, newValue) => { + setLocalParamsRange(newValue); + }, []); + + // Handle input change + const handleInputChange = useCallback( + (index) => (event) => { + const value = event.target.value === "" ? "" : Number(event.target.value); + if (value === "" || (value >= -1 && value <= 140)) { + const newRange = [...localParamsRange]; + newRange[index] = value; + setLocalParamsRange(newRange); + } + }, + [localParamsRange] + ); + + // Sync local state with props + useEffect(() => { + setLocalParamsRange(paramsRange); + }, [paramsRange]); + + // Propagate changes to parent after delay + useEffect(() => { + if (stableTimerRef.current) { + clearTimeout(stableTimerRef.current); + } + + stableTimerRef.current = setTimeout(() => { + if (Array.isArray(localParamsRange) && localParamsRange.length === 2) { + onParamsRangeChange(localParamsRange); + } + }, 300); + + return () => { + if (stableTimerRef.current) { + clearTimeout(stableTimerRef.current); + } + }; + }, [localParamsRange, onParamsRangeChange]); + + const renderContent = () => { + if (title === "Parameters") { + return ( + + + + + + + + (value === -1 ? "All" : `${value}B`)} + sx={{ + "& .MuiSlider-rail": { + height: 10, + backgroundColor: "background.paper", + border: "1px solid", + borderColor: "divider", + opacity: 1, + }, + "& .MuiSlider-track": { + height: 10, + border: "1px solid", + borderColor: (theme) => + alpha( + theme.palette.primary.main, + theme.palette.mode === "light" ? 0.3 : 0.5 + ), + backgroundColor: (theme) => + alpha( + theme.palette.primary.main, + theme.palette.mode === "light" ? 0.1 : 0.2 + ), + }, + "& .MuiSlider-thumb": { + width: 20, + height: 20, + backgroundColor: "background.paper", + border: "1px solid", + borderColor: "primary.main", + "&:hover, &.Mui-focusVisible": { + boxShadow: (theme) => + `0 0 0 8px ${alpha( + theme.palette.primary.main, + theme.palette.mode === "light" ? 0.08 : 0.16 + )}`, + }, + "&.Mui-active": { + boxShadow: (theme) => + `0 0 0 12px ${alpha( + theme.palette.primary.main, + theme.palette.mode === "light" ? 0.08 : 0.16 + )}`, + }, + }, + "& .MuiSlider-valueLabel": { + backgroundColor: theme.palette.primary.main, + }, + "& .MuiSlider-mark": { + width: 2, + height: 10, + backgroundColor: "divider", + }, + "& .MuiSlider-markLabel": { + fontSize: "0.875rem", + "&::after": { + content: '"B"', + marginLeft: "1px", + opacity: 0.5, + }, + '&[data-index="0"]::after': { + content: '""', + }, + }, + }} + /> + + ); + } + return ( + + {children} + + ); + }; + + return ( + + + + {title} + + + + {renderContent()} + + ); +}; + +const CustomCollapse = forwardRef((props, ref) => { + const { children, style = {}, ...other } = props; + const collapsedHeight = "0px"; + const timeout = 300; + + const wrapperRef = useRef(null); + const [animatedHeight, setAnimatedHeight] = useState( + props.in ? "auto" : collapsedHeight + ); + + useEffect(() => { + if (!wrapperRef.current) return; + + if (props.in) { + const contentHeight = wrapperRef.current.scrollHeight; + setAnimatedHeight(`${contentHeight}px`); + } else { + setAnimatedHeight(collapsedHeight); + } + }, [props.in, children]); + + const handleEntered = (node) => { + setAnimatedHeight("auto"); + if (props.onEntered) { + props.onEntered(node); + } + }; + + return ( + +
{children}
+
+ ); +}); + +const LeaderboardFilters = ({ + selectedPrecisions = FILTER_PRECISIONS, + onPrecisionsChange = () => {}, + selectedTypes = MODEL_TYPE_ORDER, + onTypesChange = () => {}, + paramsRange = [-1, 140], + onParamsRangeChange = () => {}, + selectedBooleanFilters = [], + onBooleanFiltersChange = () => {}, + data = [], + expanded, + onToggleExpanded, + loading = false, +}) => { + const [localParamsRange, setLocalParamsRange] = useState(paramsRange); + const stableTimerRef = useRef(null); + const { state, actions } = useLeaderboard(); + const { normal: filterCounts, officialOnly: officialOnlyCounts } = + state.filterCounts; + const isOfficialProviderActive = state.filters.isOfficialProviderActive; + const currentCounts = useMemo( + () => (isOfficialProviderActive ? officialOnlyCounts : filterCounts), + [isOfficialProviderActive, officialOnlyCounts, filterCounts] + ); + + useEffect(() => { + setLocalParamsRange(paramsRange); + }, [paramsRange]); + + // Clean up timer when component unmounts + useEffect(() => { + return () => { + if (stableTimerRef.current) { + clearTimeout(stableTimerRef.current); + } + }; + }, []); + + const handleParamsRangeChange = (event, newValue) => { + setLocalParamsRange(newValue); + }; + + const handleParamsRangeChangeCommitted = (event, newValue) => { + // Reset timer on each change + if (stableTimerRef.current) { + clearTimeout(stableTimerRef.current); + } + + // Update URL immediately + onParamsRangeChange(newValue); + + // Trigger data update after debounce + stableTimerRef.current = setTimeout(() => { + actions.updateFilteredData(); + }, TABLE_DEFAULTS.DEBOUNCE.SEARCH); + }; + + const handlePrecisionToggle = (precision) => { + const newPrecisions = selectedPrecisions.includes(precision) + ? selectedPrecisions.filter((p) => p !== precision) + : [...selectedPrecisions, precision]; + onPrecisionsChange(newPrecisions); + }; + + const handleBooleanFilterToggle = (filter) => { + const newFilters = selectedBooleanFilters.includes(filter) + ? selectedBooleanFilters.filter((f) => f !== filter) + : [...selectedBooleanFilters, filter]; + onBooleanFiltersChange(newFilters); + }; + + // Filter options based on their hide property + const showFilterOptions = BOOLEAN_FILTER_OPTIONS.filter( + (option) => !option.hide + ); + const hideFilterOptions = BOOLEAN_FILTER_OPTIONS.filter( + (option) => option.hide + ); + + const handleOfficialProviderToggle = () => { + actions.toggleOfficialProvider(); + }; + + return loading ? null : ( + + + + + + + + alpha(theme.palette.primary.main, 0.02), + border: "1px solid", + borderColor: (theme) => + alpha(theme.palette.primary.main, 0.2), + borderRadius: 1, + p: 3, + position: "relative", + width: "100%", + display: "flex", + flexDirection: "column", + "&:hover": { + borderColor: (theme) => + alpha(theme.palette.primary.main, 0.3), + backgroundColor: (theme) => + alpha(theme.palette.primary.main, 0.03), + }, + transition: (theme) => + theme.transitions.create( + ["border-color", "background-color"], + { + duration: theme.transitions.duration.short, + } + ), + }} + > + + Advanced Filters + + + + + + + {FILTER_PRECISIONS.map((precision) => ( + + handlePrecisionToggle(precision) + } + count={currentCounts.precisions[precision]} + showCheckbox={true} + /> + ))} + + + + + + + + + + + alpha( + theme.palette.primary.main, + theme.palette.mode === "light" + ? 0.3 + : 0.5 + ), + backgroundColor: (theme) => + alpha( + theme.palette.primary.main, + theme.palette.mode === "light" + ? 0.1 + : 0.2 + ), + }, + "& .MuiSlider-thumb": { + width: 20, + height: 20, + backgroundColor: "background.paper", + border: "1px solid", + borderColor: "primary.main", + "&:hover, &.Mui-focusVisible": { + boxShadow: (theme) => + `0 0 0 8px ${alpha( + theme.palette.primary.main, + theme.palette.mode === "light" + ? 0.08 + : 0.16 + )}`, + }, + "&.Mui-active": { + boxShadow: (theme) => + `0 0 0 12px ${alpha( + theme.palette.primary.main, + theme.palette.mode === "light" + ? 0.08 + : 0.16 + )}`, + }, + }, + "& .MuiSlider-mark": { + backgroundColor: "text.disabled", + height: 2, + width: 2, + borderRadius: "50%", + }, + "& .MuiSlider-markLabel": { + color: "text.secondary", + }, + }} + /> + + + + + + + {/* Deuxième ligne */} + + + + {MODEL_TYPE_ORDER.sort( + (a, b) => + MODEL_TYPES[a].order - MODEL_TYPES[b].order + ).map((type) => ( + { + const newTypes = selectedTypes.includes(type) + ? selectedTypes.filter((t) => t !== type) + : [...selectedTypes, type]; + onTypesChange(newTypes); + }} + count={currentCounts.modelTypes[type]} + variant="tag" + showCheckbox={true} + /> + ))} + + + + + + + + {hideFilterOptions.map((filter) => ( + { + const newFilters = + selectedBooleanFilters.includes( + filter.value + ) + ? selectedBooleanFilters.filter( + (f) => f !== filter.value + ) + : [ + ...selectedBooleanFilters, + filter.value, + ]; + onBooleanFiltersChange(newFilters); + }} + count={ + filter.value === "is_moe" + ? currentCounts.mixtureOfExperts + : filter.value === "is_flagged" + ? currentCounts.flagged + : filter.value === "is_merged" + ? currentCounts.merged + : filter.value === "is_not_available_on_hub" + ? currentCounts.notOnHub + : 0 + } + isHideFilter={false} + totalCount={data.length} + showCheckbox={true} + /> + ))} + + + + + + + + + + + alpha(theme.palette.secondary.main, 0.02), + border: "1px solid", + borderColor: (theme) => + alpha(theme.palette.secondary.main, 0.15), + borderRadius: 1, + p: 3, + position: "relative", + width: "100%", + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + textAlign: "center", + minHeight: "100%", + "&:hover": { + borderColor: (theme) => + alpha(theme.palette.secondary.main, 0.25), + backgroundColor: (theme) => + alpha(theme.palette.secondary.main, 0.03), + }, + transition: (theme) => + theme.transitions.create( + ["border-color", "background-color"], + { + duration: theme.transitions.duration.short, + } + ), + }} + > + + + Official Models + + + Show only models that are officially provided and + maintained by their original creators. + + + {showFilterOptions.map((filter) => ( + + handleBooleanFilterToggle(filter.value) + } + count={ + filter.value === "is_highlighted_by_maintainer" + ? currentCounts.maintainersHighlight + : 0 + } + showCheckbox={true} + variant="secondary" + /> + + + {( + filter.value === "is_highlighted_by_maintainer" + ? isOfficialProviderActive + : selectedBooleanFilters.includes(filter.value) + ) + ? "Filter active" + : "Filter inactive"} + + + ))} + + + + + + + + + + ); +}; + +export default LeaderboardFilters; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/QuickFilters.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/QuickFilters.js new file mode 100644 index 0000000000000000000000000000000000000000..b80c8295a681a3dc32d1820b4152561eba2b5301 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/QuickFilters.js @@ -0,0 +1,226 @@ +import React, { useCallback, useMemo } from "react"; +import { Box, Typography, Skeleton } from "@mui/material"; +import { alpha } from "@mui/material/styles"; +import { QUICK_FILTER_PRESETS } from "../../constants/quickFilters"; +import FilterTag from "../../../../../../components/shared/FilterTag"; +import { useLeaderboard } from "../../context/LeaderboardContext"; +import InfoIconWithTooltip from "../../../../../../components/shared/InfoIconWithTooltip"; +import { UI_TOOLTIPS } from "../../constants/tooltips"; + +const QuickFiltersTitle = ({ sx = {} }) => ( + + + Quick Filters + + + +); + +export const QuickFiltersSkeleton = () => ( + + ({ + xs: alpha(theme.palette.primary.main, 0.02), + lg: "transparent", + }), + borderColor: (theme) => ({ + xs: alpha(theme.palette.primary.main, 0.2), + lg: "transparent", + }), + border: "1px solid", + borderRadius: 1, + p: 3, + display: "flex", + flexDirection: { xs: "column", md: "column", lg: "row" }, + gap: 2, + mb: 2, + width: "100%", + }} + > + + + {[1, 2, 3, 4].map((i) => ( + + ))} + + + +); + +const QuickFilters = ({ totalCount = 0, loading = false }) => { + const { state, actions } = useLeaderboard(); + const { normal: filterCounts, officialOnly: officialOnlyCounts } = + state.filterCounts; + const isOfficialProviderActive = state.filters.isOfficialProviderActive; + const currentParams = state.filters.paramsRange; + + const currentCounts = useMemo( + () => (isOfficialProviderActive ? officialOnlyCounts : filterCounts), + [isOfficialProviderActive, officialOnlyCounts, filterCounts] + ); + + const modelSizePresets = useMemo( + () => + QUICK_FILTER_PRESETS.filter( + (preset) => preset.id !== "official_providers" + ), + [] + ); + + const officialProvidersPreset = useMemo( + () => + QUICK_FILTER_PRESETS.find((preset) => preset.id === "official_providers"), + [] + ); + + const handleSizePresetClick = useCallback( + (preset) => { + const isActive = + currentParams[0] === preset.filters.paramsRange[0] && + currentParams[1] === preset.filters.paramsRange[1]; + + if (isActive) { + actions.setFilter("paramsRange", [-1, 140]); // Reset to default + } else { + actions.setFilter("paramsRange", preset.filters.paramsRange); + } + }, + [currentParams, actions] + ); + + const getPresetCount = useCallback( + (preset) => { + const range = preset.id.split("_")[0]; + return currentCounts.parameterRanges[range] || 0; + }, + [currentCounts] + ); + + const handleOfficialProviderToggle = useCallback(() => { + actions.toggleOfficialProvider(); + }, [actions]); + + if (loading) { + return ; + } + + return ( + + ({ + xs: alpha(theme.palette.primary.main, 0.02), + lg: "transparent", + }), + borderColor: (theme) => ({ + xs: alpha(theme.palette.primary.main, 0.2), + lg: "transparent", + }), + border: "1px solid", + borderRadius: 1, + p: 3, + display: "flex", + flexDirection: { xs: "column", lg: "row" }, + alignItems: "center", + gap: 2, + width: "100%", + }} + > + + + + div": { + width: { xs: "100%", md: 0, lg: "auto" }, + flex: { + xs: "auto", + md: "1 1 0", + lg: "0 0 auto", + }, + }, + }} + > + {modelSizePresets.map((preset) => ( + handleSizePresetClick(preset)} + count={getPresetCount(preset)} + totalCount={totalCount} + /> + ))} + + + + {officialProvidersPreset && ( + + )} + + + + ); +}; + +QuickFilters.displayName = "QuickFilters"; + +export default React.memo(QuickFilters); diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/SearchBar.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/SearchBar.js new file mode 100644 index 0000000000000000000000000000000000000000..c32cd8f8640b0d2e8fa7c1928f76fcd8d53fe494 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/SearchBar.js @@ -0,0 +1,329 @@ +import React, { useState, useEffect } from "react"; +import { Box, InputBase, Typography, Paper, Skeleton } from "@mui/material"; + +import SearchIcon from "@mui/icons-material/Search"; +import FilterListIcon from "@mui/icons-material/FilterList"; +import RestartAltIcon from "@mui/icons-material/RestartAlt"; +import { useTheme } from "@mui/material/styles"; +import { generateSearchDescription } from "../../utils/searchUtils"; +import { + HIGHLIGHT_COLORS, + TABLE_DEFAULTS, + FILTER_PRECISIONS, +} from "../../constants/defaults"; +import { MODEL_TYPE_ORDER } from "../../constants/modelTypes"; +import { alpha } from "@mui/material/styles"; +import FilteredModelCount from "./FilteredModelCount"; +import { useLeaderboard } from "../../context/LeaderboardContext"; +import InfoIconWithTooltip from "../../../../../../components/shared/InfoIconWithTooltip"; +import { UI_TOOLTIPS } from "../../constants/tooltips"; + +export const SearchBarSkeleton = () => ( + + alpha(theme.palette.background.paper, 0.8), + borderRadius: 1, + border: (theme) => + `1px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.05 : 0.1 + )}`, + display: "flex", + alignItems: "center", + px: 2, + gap: 2, + }} + > + + + + + + + + + + + Supports strict search and regex • Use semicolons for multiple terms + + + +); + +const SearchDescription = ({ searchValue }) => { + const searchGroups = generateSearchDescription(searchValue); + + if (!searchGroups || searchGroups.length === 0) return null; + + return ( + + + Showing models matching: + + {searchGroups.map(({ text, index }, i) => ( + + {i > 0 && ( + + and + + )} + + theme.palette.getContrastText( + HIGHLIGHT_COLORS[index % HIGHLIGHT_COLORS.length] + ), + padding: "2px 4px", + borderRadius: "4px", + fontSize: "0.85rem", + fontWeight: 500, + }} + > + {text} + + + ))} + + ); +}; + +const SearchBar = ({ + onToggleFilters, + filtersOpen, + loading = false, + data = [], + table = null, +}) => { + const theme = useTheme(); + const { state, actions } = useLeaderboard(); + const [localValue, setLocalValue] = useState(state.filters.search); + + useEffect(() => { + setLocalValue(state.filters.search); + }, [state.filters.search]); + + useEffect(() => { + const timer = setTimeout(() => { + if (localValue !== state.filters.search) { + actions.setFilter("search", localValue); + } + }, TABLE_DEFAULTS.DEBOUNCE.SEARCH); + + return () => clearTimeout(timer); + }, [localValue, state.filters.search, actions]); + + const handleLocalChange = (e) => { + setLocalValue(e.target.value); + }; + + const hasActiveFilters = + Object.values(state.filters.booleanFilters).some((value) => value) || + state.filters.precisions.length !== FILTER_PRECISIONS.length || + state.filters.types.length !== MODEL_TYPE_ORDER.length || + state.filters.paramsRange[0] !== -1 || + state.filters.paramsRange[1] !== 140 || + state.filters.isOfficialProviderActive; + + const shouldShowReset = localValue || hasActiveFilters; + + return ( + + + + + {!loading && ( + + )} + + {shouldShowReset && ( + { + setLocalValue(""); + actions.resetFilters(); + }} + sx={{ + display: "flex", + alignItems: "center", + gap: 0.5, + cursor: "pointer", + color: "text.secondary", + backgroundColor: "transparent", + border: "1px solid", + borderColor: "divider", + borderRadius: 1, + padding: "4px 8px", + "&:hover": { + backgroundColor: "action.hover", + color: "text.primary", + }, + userSelect: "none", + transition: "all 0.2s ease", + }} + > + + + Reset + + + )} + + + + Advanced Filters + + + + + + + {localValue ? ( + + ) : ( + + + Supports strict search and regex • Use semicolons for multiple + terms + + + )} + + + ); +}; + +export default SearchBar; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/hooks/useOfficialProvidersMode.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/hooks/useOfficialProvidersMode.js new file mode 100644 index 0000000000000000000000000000000000000000..4aced06d406e9bfcf6d5591fd9d3711135bbb905 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/hooks/useOfficialProvidersMode.js @@ -0,0 +1,130 @@ +import { useCallback, useState, useEffect, useRef } from "react"; +import { useSearchParams } from "react-router-dom"; + +const useRouterSearchParams = () => { + try { + return useSearchParams(); + } catch { + return [null, () => {}]; + } +}; + +export const useOfficialProvidersMode = () => { + const [isOfficialProviderActive, setIsOfficialProviderActive] = + useState(false); + const [searchParams, setSearchParams] = useRouterSearchParams(); + const normalFiltersRef = useRef(null); + const isInitialLoadRef = useRef(true); + const lastToggleSourceRef = useRef(null); + + // Effect to handle initial state and updates + useEffect(() => { + if (!searchParams) return; + + const filters = searchParams.get("filters"); + const isHighlighted = + filters?.includes("is_highlighted_by_maintainer") || false; + + // On initial load + if (isInitialLoadRef.current) { + isInitialLoadRef.current = false; + + // If official mode is active at start, store filters without the highlightFilter + if (isHighlighted && filters) { + const initialNormalFilters = filters + .split(",") + .filter((f) => f !== "is_highlighted_by_maintainer" && f !== "") + .filter(Boolean); + if (initialNormalFilters.length > 0) { + normalFiltersRef.current = initialNormalFilters.join(","); + } + } + + // Update state without triggering URL change + setIsOfficialProviderActive(isHighlighted); + return; + } + + // For subsequent changes + if (!isHighlighted && filters) { + normalFiltersRef.current = filters; + } + + setIsOfficialProviderActive(isHighlighted); + }, [searchParams]); + + const toggleOfficialProviderMode = useCallback( + (source = null) => { + if (!searchParams || !setSearchParams) return; + + // If source is the same as last time and last change was less than 100ms ago, ignore + const now = Date.now(); + if ( + source && + source === lastToggleSourceRef.current?.source && + now - (lastToggleSourceRef.current?.timestamp || 0) < 100 + ) { + return; + } + + const currentFiltersStr = searchParams.get("filters"); + const currentFilters = + currentFiltersStr?.split(",").filter(Boolean) || []; + const highlightFilter = "is_highlighted_by_maintainer"; + const newSearchParams = new URLSearchParams(searchParams); + + if (currentFilters.includes(highlightFilter)) { + // Deactivating official provider mode + if (normalFiltersRef.current) { + const normalFilters = normalFiltersRef.current + .split(",") + .filter((f) => f !== highlightFilter && f !== "") + .filter(Boolean); + + if (normalFilters.length > 0) { + newSearchParams.set("filters", normalFilters.join(",")); + } else { + newSearchParams.delete("filters"); + } + } else { + const newFilters = currentFilters.filter( + (f) => f !== highlightFilter && f !== "" + ); + if (newFilters.length === 0) { + newSearchParams.delete("filters"); + } else { + newSearchParams.set("filters", newFilters.join(",")); + } + } + } else { + // Activating official provider mode + if (currentFiltersStr) { + normalFiltersRef.current = currentFiltersStr; + } + + const filtersToSet = [ + ...new Set([...currentFilters, highlightFilter]), + ].filter(Boolean); + newSearchParams.set("filters", filtersToSet.join(",")); + } + + // Update state immediately + setIsOfficialProviderActive(!currentFilters.includes(highlightFilter)); + + // Save source and timestamp of last change + lastToggleSourceRef.current = { + source, + timestamp: now, + }; + + // Update search params and let HashRouter handle the URL + setSearchParams(newSearchParams); + }, + [searchParams, setSearchParams] + ); + + return { + isOfficialProviderActive, + toggleOfficialProviderMode, + }; +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/hooks/usePresets.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/hooks/usePresets.js new file mode 100644 index 0000000000000000000000000000000000000000..35e17e54b0e1978635440908d3de6c742b37a856 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/hooks/usePresets.js @@ -0,0 +1,98 @@ +import { useCallback } from "react"; +import { QUICK_FILTER_PRESETS } from "../../../constants/quickFilters"; +import { TABLE_DEFAULTS } from "../../../constants/defaults"; + +const DEFAULT_FILTERS = { + searchValue: "", + selectedPrecisions: TABLE_DEFAULTS.SEARCH.PRECISIONS, + selectedTypes: TABLE_DEFAULTS.SEARCH.TYPES, + paramsRange: TABLE_DEFAULTS.SEARCH.PARAMS_RANGE, + selectedBooleanFilters: [], +}; + +export const usePresets = (searchFilters) => { + const handlePresetChange = useCallback( + (preset) => { + if (!searchFilters?.batchUpdateState) return; + + if (preset === null) { + // Reset with default values + searchFilters.batchUpdateState(DEFAULT_FILTERS, true); + return; + } + + // Apply preset with default values as base + const updates = { + ...DEFAULT_FILTERS, + ...preset.filters, + }; + + // Apply all changes at once + searchFilters.batchUpdateState(updates, true); + }, + [searchFilters] + ); + + const resetPreset = useCallback(() => { + handlePresetChange(null); + }, [handlePresetChange]); + + const getActivePreset = useCallback(() => { + // If searchFilters is not initialized yet, return null + if (!searchFilters) return null; + + // Dynamic detection of preset matching current filters + const currentParamsRange = Array.isArray(searchFilters.paramsRange) + ? searchFilters.paramsRange + : DEFAULT_FILTERS.paramsRange; + const currentBooleanFilters = Array.isArray( + searchFilters.selectedBooleanFilters + ) + ? searchFilters.selectedBooleanFilters + : DEFAULT_FILTERS.selectedBooleanFilters; + const currentPrecisions = Array.isArray(searchFilters.selectedPrecisions) + ? searchFilters.selectedPrecisions + : DEFAULT_FILTERS.selectedPrecisions; + const currentTypes = Array.isArray(searchFilters.selectedTypes) + ? searchFilters.selectedTypes + : DEFAULT_FILTERS.selectedTypes; + + return ( + QUICK_FILTER_PRESETS.find((preset) => { + const presetParamsRange = Array.isArray(preset.filters.paramsRange) + ? preset.filters.paramsRange + : DEFAULT_FILTERS.paramsRange; + const presetBooleanFilters = Array.isArray( + preset.filters.selectedBooleanFilters + ) + ? preset.filters.selectedBooleanFilters + : DEFAULT_FILTERS.selectedBooleanFilters; + + const paramsMatch = + JSON.stringify(presetParamsRange) === + JSON.stringify(currentParamsRange); + const booleanFiltersMatch = + JSON.stringify(presetBooleanFilters.sort()) === + JSON.stringify(currentBooleanFilters.sort()); + + // Check if other filters match default values + const precisionMatch = + JSON.stringify(currentPrecisions.sort()) === + JSON.stringify(DEFAULT_FILTERS.selectedPrecisions.sort()); + const typesMatch = + JSON.stringify(currentTypes.sort()) === + JSON.stringify(DEFAULT_FILTERS.selectedTypes.sort()); + + return ( + paramsMatch && booleanFiltersMatch && precisionMatch && typesMatch + ); + })?.id || null + ); + }, [searchFilters]); + + return { + activePreset: getActivePreset(), + handlePresetChange, + resetPreset, + }; +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/PerformanceMonitor.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/PerformanceMonitor.js new file mode 100644 index 0000000000000000000000000000000000000000..d3a20d28639f0d84835d854fe405795e14499d01 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/PerformanceMonitor.js @@ -0,0 +1,570 @@ +import React, { useEffect, useState, useRef } from "react"; +import { Box, Typography, Tooltip, useTheme } from "@mui/material"; +import NetworkCheckIcon from "@mui/icons-material/NetworkCheck"; +import MemoryIcon from "@mui/icons-material/Memory"; +import SpeedIcon from "@mui/icons-material/Speed"; +import GpuIcon from "@mui/icons-material/Memory"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; + +const getGPUStats = () => { + try { + const canvas = document.createElement("canvas"); + const gl = + canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); + + if (!gl) { + canvas.remove(); + return null; + } + + // Try to get GPU info extensions + const debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); + + // Estimate GPU memory usage (very approximate) + let usedMemoryEstimate = 0; + + try { + // Create test texture + const testTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, testTexture); + + // Test size: 1024x1024 RGBA + const testSize = 1024; + const pixels = new Uint8Array(testSize * testSize * 4); + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + testSize, + testSize, + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + pixels + ); + + // Estimate memory usage (very approximate) + usedMemoryEstimate = (testSize * testSize * 4) / (1024 * 1024); // In MB + + gl.deleteTexture(testTexture); + gl.getExtension("WEBGL_lose_context")?.loseContext(); + } catch (e) { + console.warn("GPU memory estimation failed:", e); + } finally { + // Cleanup WebGL resources + const loseContext = gl.getExtension("WEBGL_lose_context"); + if (loseContext) loseContext.loseContext(); + gl.canvas.remove(); + } + + return { + vendor: debugInfo + ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) + : "Unknown", + renderer: debugInfo + ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) + : "Unknown", + usedMemory: Math.round(usedMemoryEstimate), + }; + } catch (e) { + return null; + } +}; + +const MetricBox = ({ icon, label, value, tooltip }) => { + const theme = useTheme(); + return ( + + {icon} + + + {label} + + + {React.isValidElement(value) ? value : {value}} + + {tooltip && ( + + + + + + )} + + ); +}; + +const formatNumber = (num) => { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " "); +}; + +const PerformanceMonitor = () => { + const theme = useTheme(); + + const [stats, setStats] = useState({ + fps: 0, + memory: { + usedJSHeapSize: 0, + totalJSHeapSize: 0, + }, + renders: 0, + network: { + transferSize: 0, + decodedBodySize: 0, + compressionRatio: 0, + }, + gpu: getGPUStats(), + fcp: null, + }); + const [isVisible, setIsVisible] = useState( + process.env.NODE_ENV === "development" + ); + const renderCountRef = useRef(0); + const originalCreateElementRef = useRef(null); + + useEffect(() => { + const handleKeyDown = (event) => { + // Ignore if user is in an input field + if ( + event.target.tagName === "INPUT" || + event.target.tagName === "TEXTAREA" + ) { + return; + } + + if (event.key === "p" || event.key === "P") { + setIsVisible((prev) => !prev); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, []); + + useEffect(() => { + let frameCount = 0; + let lastTime = performance.now(); + let animationFrameId; + + const getNetworkStats = () => { + const resources = performance.getEntriesByType("resource"); + const navigation = performance.getEntriesByType("navigation")[0]; + + let totalTransferSize = navigation ? navigation.transferSize : 0; + let totalDecodedSize = navigation ? navigation.decodedBodySize : 0; + + resources.forEach((resource) => { + totalTransferSize += resource.transferSize || 0; + totalDecodedSize += resource.decodedBodySize || 0; + }); + + const compressionRatio = totalDecodedSize + ? Math.round((1 - totalTransferSize / totalDecodedSize) * 100) + : 0; + + return { + transferSize: Math.round(totalTransferSize / 1024), + decodedBodySize: Math.round(totalDecodedSize / 1024), + compressionRatio, + }; + }; + + // Save original function + originalCreateElementRef.current = React.createElement; + + // Replace createElement + React.createElement = function (...args) { + renderCountRef.current++; + return originalCreateElementRef.current.apply(this, args); + }; + + const updateStats = () => { + frameCount++; + const now = performance.now(); + const delta = now - lastTime; + + if (delta >= 1000) { + const fps = Math.round((frameCount * 1000) / delta); + + const memory = window.performance?.memory + ? { + usedJSHeapSize: Math.round( + window.performance.memory.usedJSHeapSize / 1048576 + ), + totalJSHeapSize: Math.round( + window.performance.memory.totalJSHeapSize / 1048576 + ), + } + : null; + + const network = getNetworkStats(); + const gpu = getGPUStats(); + + setStats((prev) => ({ + ...prev, + fps, + memory: memory || prev.memory, + renders: renderCountRef.current, + network, + gpu, + })); + + frameCount = 0; + lastTime = now; + } + + animationFrameId = requestAnimationFrame(updateStats); + }; + + updateStats(); + + return () => { + cancelAnimationFrame(animationFrameId); + // Restore original function + if (originalCreateElementRef.current) { + React.createElement = originalCreateElementRef.current; + } + // Clean up counters + renderCountRef.current = 0; + delete window.__REACT_RENDERS__; + }; + }, []); + + useEffect(() => { + // Add FCP observer + if (window.PerformanceObserver) { + try { + const fcpObserver = new PerformanceObserver((entryList) => { + const entries = entryList.getEntries(); + if (entries.length > 0) { + const fcp = entries[0].startTime; + setStats((prev) => ({ + ...prev, + fcp, + })); + } + }); + + fcpObserver.observe({ entryTypes: ["paint"] }); + return () => fcpObserver.disconnect(); + } catch (e) { + console.warn("FCP observation failed:", e); + } + } + }, []); + + const getFpsColor = (fps) => { + if (fps >= 55) return "#4CAF50"; + if (fps >= 30) return "#FFC107"; + return "#F44336"; + }; + + return isVisible ? ( + + + + Performances{" "} + dev only + + + {/* Performance Metrics */} + + + } + label="FPS" + value={ + + {stats.fps} + + } + tooltip="Frames Per Second - Indicates how smooth the UI is running" + /> + + {stats.fcp !== null && ( + + } + label="FCP" + value={ + + {Math.round(stats.fcp)}ms + + } + tooltip="First Contentful Paint - Time until first content is rendered" + /> + )} + + ⚛️ + + } + label="React" + value={ + + {formatNumber(stats.renders)} + cycles + + } + tooltip="Total number of React render cycles" + /> + + + {/* Memory Metrics */} + + {window.performance?.memory && ( + } + label="Mem" + value={ + + {stats.memory.usedJSHeapSize} + / + {stats.memory.totalJSHeapSize} + MB + + } + tooltip="JavaScript heap memory usage (Used / Total)" + /> + )} + {stats.gpu && ( + } + label="GPU" + value={ + + {stats.gpu.usedMemory} + MB + + } + tooltip="Estimated GPU memory usage" + /> + )} + + + {/* Network Metrics */} + + + } + label="Net" + value={ + + {stats.network.transferSize} + KB + + } + tooltip="Network data transferred" + /> + } + label="Size" + value={ + + {formatNumber(stats.network.decodedBodySize)} + KB + 0 ? "#81C784" : "inherit", + fontSize: "0.7rem", + opacity: 0.8, + ml: 1, + }} + > + (-{stats.network.compressionRatio}%) + + + } + tooltip="Total decoded size and compression ratio" + /> + + + Press "P" to show/hide + + + +
+ ) : null; +}; + +export default React.memo(PerformanceMonitor); diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/Table.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/Table.js new file mode 100644 index 0000000000000000000000000000000000000000..b9279247881135a2d4cf2122ed542474fc20f6be --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/Table.js @@ -0,0 +1,720 @@ +import React, { useRef, useCallback, useMemo } from "react"; +import { + Paper, + Table, + TableContainer, + TableHead, + TableBody, + TableRow, + TableCell, + Box, + Typography, + Skeleton, +} from "@mui/material"; +import { flexRender } from "@tanstack/react-table"; +import { useVirtualizer } from "@tanstack/react-virtual"; +import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore"; +import SearchOffIcon from "@mui/icons-material/SearchOff"; +import { + TABLE_DEFAULTS, + ROW_SIZES, + SKELETON_COLUMNS, +} from "../../constants/defaults"; +import { alpha } from "@mui/material/styles"; +import TableOptions from "../DisplayOptions/DisplayOptions"; +import ColumnSelector from "../ColumnSelector/ColumnSelector"; + +const NoResultsFound = () => ( + + + + No models found + + + Try modifying your filters or search to see more models. + + +); + +const TableSkeleton = ({ rowSize = "normal" }) => { + const currentRowHeight = Math.floor(ROW_SIZES[rowSize]); + const headerHeight = Math.floor(currentRowHeight * 1.25); + const skeletonRows = 10; + + return ( + + `1px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.05 : 0.1 + )}`, + borderRadius: 1, + }} + > + + + + {SKELETON_COLUMNS.map((width, index) => ( + 3 ? "right" : "left", + borderRight: (theme) => `1px solid ${theme.palette.divider}`, + "&:last-child": { + borderRight: "none", + }, + position: "sticky", + top: 0, + backgroundColor: (theme) => theme.palette.background.paper, + zIndex: 2, + }} + /> + ))} + + + + {[...Array(skeletonRows)].map((_, index) => ( + + index % 2 === 0 ? "transparent" : theme.palette.action.hover, + }} + > + {SKELETON_COLUMNS.map((width, cellIndex) => ( + + `1px solid ${theme.palette.divider}`, + "&:last-child": { + borderRight: "none", + }, + }} + > + 3 ? "auto" : 0, + backgroundColor: (theme) => + alpha(theme.palette.text.primary, 0.11), + "&::after": { + background: (theme) => + `linear-gradient(90deg, ${alpha( + theme.palette.text.primary, + 0.11 + )}, ${alpha( + theme.palette.text.primary, + 0.14 + )}, ${alpha(theme.palette.text.primary, 0.11)})`, + }, + }} + /> + + ))} + + ))} + +
+
+ ); +}; + +const TableControls = React.memo( + ({ + loading, + rowSize, + onRowSizeChange, + scoreDisplay, + onScoreDisplayChange, + averageMode, + onAverageModeChange, + rankingMode, + onRankingModeChange, + hasTableOptionsChanges, + searchParams, + setSearchParams, + table, + handleColumnReset, + hasColumnFilterChanges, + onColumnVisibilityChange, + }) => ( + + + + + ) +); + +TableControls.displayName = "TableControls"; + +const LeaderboardTable = ({ + table, + rowSize = "normal", + loading = false, + hasTableOptionsChanges, + hasColumnFilterChanges, + onColumnVisibilityChange, + scoreDisplay, + onScoreDisplayChange, + averageMode, + onAverageModeChange, + rankingMode, + onRankingModeChange, + onRowSizeChange, + searchParams, + setSearchParams, + pinnedModels = [], +}) => { + const { rows } = table.getRowModel(); + const parentRef = useRef(null); + + const currentRowHeight = useMemo(() => ROW_SIZES[rowSize], [rowSize]); + const headerHeight = useMemo( + () => Math.floor(currentRowHeight * 1.25), + [currentRowHeight] + ); + + // Separate pinned rows from normal rows while preserving original order + const pinnedRows = useMemo(() => { + const pinnedModelRows = rows.filter((row) => row.original.isPinned); + // Sort pinned models according to their original order in pinnedModels + return pinnedModelRows.sort((a, b) => { + const aIndex = pinnedModels.indexOf(a.original.id); + const bIndex = pinnedModels.indexOf(b.original.id); + return aIndex - bIndex; + }); + }, [rows, pinnedModels]); + + const unpinnedRows = useMemo( + () => rows.filter((row) => !row.original.isPinned), + [rows] + ); + const pinnedHeight = useMemo( + () => pinnedRows.length * currentRowHeight, + [pinnedRows.length, currentRowHeight] + ); + + const virtualizerOptions = useMemo( + () => ({ + count: unpinnedRows.length, + getScrollElement: () => parentRef.current, + estimateSize: () => currentRowHeight, + overscan: 15, + scrollMode: "sync", + scrollPaddingStart: pinnedHeight, + scrollPaddingEnd: 0, + initialRect: { width: 0, height: currentRowHeight * 15 }, + }), + [currentRowHeight, unpinnedRows.length, pinnedHeight] + ); + + const rowVirtualizer = useVirtualizer(virtualizerOptions); + + const virtualRows = rowVirtualizer.getVirtualItems(); + + // Adjust paddings to account for pinned rows + const paddingTop = virtualRows.length > 0 ? virtualRows[0].start : 0; + const paddingBottom = + virtualRows.length > 0 + ? unpinnedRows.length * currentRowHeight - + virtualRows[virtualRows.length - 1].end + : 0; + + // Handle column reset + const handleColumnReset = useCallback(() => { + onColumnVisibilityChange(TABLE_DEFAULTS.COLUMNS.DEFAULT_VISIBLE); + }, [onColumnVisibilityChange]); + + const cellStyles = (theme) => ({ + borderRight: `1px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.05 : 0.1 + )}`, + "&:last-child": { + borderRight: "none", + }, + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + padding: "8px 16px", + }); + + const headerCellStyles = (theme) => ({ + ...cellStyles(theme), + padding: "6px 16px", + height: "36px", + position: "sticky !important", + top: 0, + zIndex: 10, + "& > .header-content": { + display: "flex", + alignItems: "center", + width: "100%", + gap: "4px", + flexDirection: "row", + }, + }); + + const getSortingIcon = (column) => { + if ( + column.id === "rank" || + column.id === "model_type" || + column.id === "isPinned" + ) { + return null; + } + + if (!column.getIsSorted()) { + return ; + } + return column.getIsSorted() === "desc" ? ( + + ) : ( + + ); + }; + + const renderHeaderContent = (header) => { + const sortIcon = getSortingIcon(header.column); + return ( + + {flexRender(header.column.columnDef.header, header.getContext())} + + {sortIcon || } + + + ); + }; + + const renderRow = (row, isSticky = false, stickyIndex = 0) => { + // Get row index in the sorted data model + const sortedIndex = table + .getSortedRowModel() + .rows.findIndex((r) => r.id === row.id); + + return ( + ({ + height: `${currentRowHeight}px !important`, + backgroundColor: isSticky + ? theme.palette.background.paper + : (sortedIndex + 1) % 2 === 0 + ? "transparent" + : alpha(theme.palette.mode === "dark" ? "#fff" : "#000", 0.02), + position: isSticky ? "sticky" : "relative", + top: isSticky + ? `${headerHeight + stickyIndex * currentRowHeight}px` + : "auto", + zIndex: isSticky ? 2 : 1, + boxShadow: isSticky + ? `0 1px 1px ${alpha( + theme.palette.common.black, + theme.palette.mode === "dark" ? 0.1 : 0.05 + )}` + : "none", + "&::after": isSticky + ? { + content: '""', + position: "absolute", + left: 0, + right: 0, + height: "1px", + bottom: -1, + backgroundColor: alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + ), + zIndex: 1, + } + : {}, + })} + > + {row.getVisibleCells().map((cell) => ( + ({ + width: `${cell.column.columnDef.size}px !important`, + minWidth: `${cell.column.columnDef.size}px !important`, + height: `${currentRowHeight}px`, + backgroundColor: isSticky + ? theme.palette.background.paper + : "inherit", + borderBottom: isSticky + ? "none" + : `1px solid ${theme.palette.divider}`, + ...cellStyles(theme), + ...(cell.column.columnDef.meta?.cellStyle?.(cell.getValue()) || + {}), + "& .MuiBox-root": { + overflow: "visible", + }, + })} + > + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ); + }; + + if (!loading && (!rows || rows.length === 0)) { + return ( + + + + + + + ); + } + + if (loading) { + return ( + + + + + + + ); + } + + return ( + + + + ({ + height: "100%", + overflow: "auto", + border: "none", + boxShadow: "none", + "&::-webkit-scrollbar": { + width: "8px", + height: "8px", + }, + "&::-webkit-scrollbar-thumb": { + backgroundColor: alpha( + theme.palette.common.black, + theme.palette.mode === "dark" ? 0.4 : 0.2 + ), + borderRadius: "4px", + }, + "&::-webkit-scrollbar-corner": { + backgroundColor: theme.palette.background.paper, + }, + willChange: "transform", + transform: "translateZ(0)", + WebkitOverflowScrolling: "touch", + scrollBehavior: "auto", + })} + > + 0 ? "fixed" : "fixed", + border: "none", + "& td, & th": + pinnedRows.length > 0 + ? { + width: `${100 / table.getAllColumns().length}%`, + } + : {}, + }} + > + + {table.getAllColumns().map((column, index) => ( + + ))} + + + theme.palette.background.paper, + "& th": { + backgroundColor: (theme) => theme.palette.background.paper, + }, + }} + > + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ({ + cursor: header.column.getCanSort() + ? "pointer" + : "default", + width: header.column.columnDef.size, + minWidth: header.column.columnDef.size, + ...headerCellStyles(theme), + textAlign: "left", + fontWeight: header.column.getIsSorted() ? 700 : 400, + userSelect: "none", + height: `${headerHeight}px`, + padding: `${headerHeight * 0.25}px 16px`, + backgroundColor: theme.palette.background.paper, + })} + > + {renderHeaderContent(header)} + + ))} + + ))} + + + + {/* Pinned rows */} + {pinnedRows.map((row, index) => renderRow(row, true, index))} + + {/* Padding for virtualized rows */} + {paddingTop > 0 && ( + + + + )} + + {/* Virtualized unpinned rows */} + {virtualRows.map((virtualRow) => { + const row = unpinnedRows[virtualRow.index]; + if (!row) return null; + return renderRow(row); + })} + + {/* Bottom padding */} + {paddingBottom > 0 && ( + + + + )} + +
+
+
+
+ ); +}; + +export { TableSkeleton }; +export default LeaderboardTable; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/hooks/useDataProcessing.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/hooks/useDataProcessing.js new file mode 100644 index 0000000000000000000000000000000000000000..6f5463755578ae260d6639403706e5f6071eb614 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/hooks/useDataProcessing.js @@ -0,0 +1,161 @@ +import { useMemo } from "react"; +import { + useReactTable, + getSortedRowModel, + getCoreRowModel, + getFilteredRowModel, +} from "@tanstack/react-table"; +import { createColumns } from "../../../utils/columnUtils"; +import { + useAverageRange, + useColorGenerator, + useProcessedData, + useFilteredData, + useColumnVisibility, +} from "../../../hooks/useDataUtils"; + +export const useDataProcessing = ( + data, + searchValue, + selectedPrecisions, + selectedTypes, + paramsRange, + selectedBooleanFilters, + sorting, + rankingMode, + averageMode, + visibleColumns, + scoreDisplay, + pinnedModels, + onTogglePin, + setSorting, + isOfficialProviderActive +) => { + // Call hooks directly at root level + const { minAverage, maxAverage } = useAverageRange(data); + const getColorForValue = useColorGenerator(minAverage, maxAverage); + const processedData = useProcessedData(data, averageMode, visibleColumns); + const columnVisibility = useColumnVisibility(visibleColumns); + + // Memoize filters + const filterConfig = useMemo( + () => ({ + selectedPrecisions, + selectedTypes, + paramsRange, + searchValue, + selectedBooleanFilters, + rankingMode, + pinnedModels, + isOfficialProviderActive, + }), + [ + selectedPrecisions, + selectedTypes, + paramsRange, + searchValue, + selectedBooleanFilters, + rankingMode, + pinnedModels, + isOfficialProviderActive, + ] + ); + + // Call useFilteredData at root level + const filteredData = useFilteredData( + processedData, + filterConfig.selectedPrecisions, + filterConfig.selectedTypes, + filterConfig.paramsRange, + filterConfig.searchValue, + filterConfig.selectedBooleanFilters, + filterConfig.rankingMode, + filterConfig.pinnedModels, + filterConfig.isOfficialProviderActive + ); + + // Memoize columns creation + const columns = useMemo( + () => + createColumns( + getColorForValue, + scoreDisplay, + columnVisibility, + data.length, + averageMode, + searchValue, + rankingMode, + onTogglePin + ), + [ + getColorForValue, + scoreDisplay, + columnVisibility, + data.length, + averageMode, + searchValue, + rankingMode, + onTogglePin, + ] + ); + + // Memoize table configuration + const tableConfig = useMemo( + () => ({ + data: filteredData, + columns, + state: { + sorting: Array.isArray(sorting) ? sorting : [], + columnVisibility, + }, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getSortedRowModel: getSortedRowModel(), + onSortingChange: setSorting, + enableColumnVisibility: true, + defaultColumn: { + sortingFn: (rowA, rowB, columnId) => { + const isDesc = sorting?.[0]?.desc; + + if (rowA.original.isPinned && rowB.original.isPinned) { + return ( + pinnedModels.indexOf(rowA.original.id) - + pinnedModels.indexOf(rowB.original.id) + ); + } + + if (isDesc) { + if (rowA.original.isPinned) return -1; + if (rowB.original.isPinned) return 1; + } else { + if (rowA.original.isPinned) return -1; + if (rowB.original.isPinned) return 1; + } + + const aValue = rowA.getValue(columnId); + const bValue = rowB.getValue(columnId); + + if (typeof aValue === "number" && typeof bValue === "number") { + return aValue - bValue; + } + + return String(aValue).localeCompare(String(bValue)); + }, + }, + }), + [filteredData, columns, sorting, columnVisibility, pinnedModels, setSorting] + ); + + const table = useReactTable(tableConfig); + + return { + table, + minAverage, + maxAverage, + getColorForValue, + processedData, + filteredData, + columns, + columnVisibility, + }; +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/hooks/useSorting.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/hooks/useSorting.js new file mode 100644 index 0000000000000000000000000000000000000000..b6e24b528b4938ecd52e2a61624e028d3ffc8dc0 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Table/hooks/useSorting.js @@ -0,0 +1,16 @@ +export const typeColumnSort = (rowA, rowB) => { + const aValue = rowA.getValue("model_type"); + const bValue = rowB.getValue("model_type"); + + // If both values are arrays, compare their first elements + if (Array.isArray(aValue) && Array.isArray(bValue)) { + return String(aValue[0] || "").localeCompare(String(bValue[0] || "")); + } + + // If one is array and other isn't, array comes first + if (Array.isArray(aValue)) return -1; + if (Array.isArray(bValue)) return 1; + + // If neither is array, compare as strings + return String(aValue || "").localeCompare(String(bValue || "")); +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/shared/DropdownButton.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/shared/DropdownButton.js new file mode 100644 index 0000000000000000000000000000000000000000..2badebd0fb115b1a0f78ff81abd41c2b384c9233 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/components/shared/DropdownButton.js @@ -0,0 +1,137 @@ +import React, { useState } from "react"; +import { Box, Popover, Portal, Typography, Skeleton } from "@mui/material"; +import { useTheme } from "@mui/material/styles"; +import { commonStyles } from "../../styles/common"; + +const DropdownButton = ({ + label, + icon: Icon, + closeIcon: CloseIcon, + hasChanges = false, + children, + defaultWidth = 340, + paperProps = {}, + buttonSx = {}, + loading = false, +}) => { + const theme = useTheme(); + const [anchorEl, setAnchorEl] = useState(null); + + const handleClick = (event) => { + event.stopPropagation(); + setAnchorEl(event.currentTarget); + }; + + const handleClose = (event) => { + if (event) { + event.stopPropagation(); + } + setAnchorEl(null); + }; + + if (loading) { + return ( + + ); + } + + return ( + + + {Boolean(anchorEl) && CloseIcon ? ( + + ) : ( + + )} + + {label} + + + + + theme.palette.mode === "light" + ? "rgba(0, 0, 0, 0.12)" + : "rgba(255, 255, 255, 0.12)", + borderRadius: 1, + position: "relative", + boxShadow: (theme) => + `0px 4px 20px ${ + theme.palette.mode === "light" + ? "rgba(0, 0, 0, 0.1)" + : "rgba(255, 255, 255, 0.1)" + }`, + ...paperProps.sx, + }, + ...paperProps, + }} + anchorOrigin={{ + vertical: "bottom", + horizontal: "right", + }} + transformOrigin={{ + vertical: "top", + horizontal: "right", + }} + slotProps={{ + backdrop: { + sx: { + backgroundColor: "transparent", + }, + }, + }} + > + {children} + + + + ); +}; + +export default DropdownButton; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/allowedModels.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/allowedModels.js new file mode 100644 index 0000000000000000000000000000000000000000..cf88e7e3740e3b1d7e68528d2977b75b18a65c42 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/allowedModels.js @@ -0,0 +1,32 @@ +// Define the list of allowed models to display +export const ALLOWED_MODELS = [ + "GPT-4o", + "o3-Mini", + "Deepseek-V3", + "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "meta-llama/Llama-3.1-70B-Instruct", + "google/gemma-3-4b-it", + "google/gemma-3-27b-it", + "Qwen/Qwen2.5-32B-Instruct", + "Qwen/Qwen2.5-Omni-7B", + "TheFinAI/finma-7b-full", + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview", + "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese", + "TheFinAI/FinMA-ES-Bilingual", + "TheFinAI/plutus-8B-instruct", + "Qwen-VL-MAX", + "LLaVA-1.6 Vicuna-13B", + "Deepseek-VL-7B-Chat", + "Whisper-V3", + "Qwen2-Audio-7B", + "Qwen2-Audio-7B-Instruct", + "SALMONN-7B", + "SALMONN-13B" +]; + +// Function to check if a model is in the allowed list +export const isModelAllowed = (modelName) => { + return ALLOWED_MODELS.some(allowedModel => + modelName.toLowerCase().includes(allowedModel.toLowerCase()) + ); +}; \ No newline at end of file diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/defaults.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/defaults.js new file mode 100644 index 0000000000000000000000000000000000000000..c4ae4933ab9774e639cd6732314784e12376ea14 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/defaults.js @@ -0,0 +1,364 @@ +import { MODEL_TYPE_ORDER } from "./modelTypes"; + +// Time constants (in milliseconds) +const TIME = { + CACHE_DURATION: 5 * 60 * 1000, // 5 minutes + DEBOUNCE: { + URL_PARAMS: 100, + SEARCH: 150, + RANGE_PICKER: 350, + }, +}; + +// Display constants +const DISPLAY = { + ROW_SIZES: { + normal: 45, + large: 60, + }, + SCORE_DISPLAY_OPTIONS: [ + { value: "normalized", label: "Normalized" }, + { value: "raw", label: "Raw" }, + ], + RANKING_MODE_OPTIONS: [ + { value: "static", label: "Static" }, + { value: "dynamic", label: "Dynamic" }, + ], +}; + +// Filter constants +const FILTERS = { + PRECISIONS: ["bfloat16", "float16", "4bit"], + SUBMISSION_PRECISIONS: [ + { value: "float16", label: "float16" }, + { value: "bfloat16", label: "bfloat16" }, + { value: "8bit", label: "8-bit" }, + { value: "4bit", label: "4-bit" }, + { value: "gptq", label: "GPTQ" }, + ], + PARAMS_RANGE: [-1, 140], + BOOLEAN_OPTIONS: [ + { + value: "is_moe", + label: "Mixture of Experts", + hide: true, + }, + { + value: "is_merged", + label: "Merged model", + hide: true, + }, + { + value: "is_flagged", + label: "Potentially contaminated model", + hide: true, + }, + { + value: "is_not_available_on_hub", + label: "Unavailable model", + hide: true, + }, + { + value: "is_highlighted_by_maintainer", + label: "Only Official Providers", + hide: false, + }, + ], + HIGHLIGHT_OPTIONS: [ + { + value: "is_highlighted_by_maintainer", + label: "Only Official Providers", + }, + ], +}; + +// Column size constants +const COLUMN_SIZES = { + RANK: 80, + TYPE_ICON: 50, + MODEL: 220, + AVERAGE_SCORE: 110, + OPENNESS: 150, + BENCHMARK: 110, + ARCHITECTURE: 120, + PRECISION: 90, + PARAMS: 110, + LICENSE: 100, + HUB_LINK: 80, + HEARTS: 80, + CHAT_TEMPLATE: 90, + UPLOAD_DATE: 110, + SUBMISSION_DATE: 130, + GENERATION: 100, + BASE_MODEL: 150, + CO2_COST: 100, + MOE: 80, + FLAG_STATUS: 90, + HUB_AVAILABILITY: 110, + OFFICIAL_PROVIDER: 150, +}; + +// Column definitions with organized structure +const COLUMNS = { + FIXED: { + "isPinned": { + group: "fixed", + size: 40, + defaultVisible: true, + label: "Pin", + }, + "rank": { + group: "fixed", + size: COLUMN_SIZES.RANK, + defaultVisible: true, + label: "Rank", + }, + "model_type": { + group: "fixed", + size: COLUMN_SIZES.TYPE_ICON, + defaultVisible: true, + label: "Type", + }, + "id": { + group: "fixed", + size: COLUMN_SIZES.MODEL, + defaultVisible: true, + label: "Model", + }, + "model.average_score": { + group: "fixed", + size: COLUMN_SIZES.AVERAGE_SCORE, + defaultVisible: true, + label: "Average", + }, + "model.openness": { + group: "fixed", + size: COLUMN_SIZES.OPENNESS, + defaultVisible: true, + label: "Openness", + }, + }, + EVALUATION: { + "evaluations.greek_average": { + group: "evaluation", + size: COLUMN_SIZES.BENCHMARK, + defaultVisible: true, + label: "Greek Financial LLM Leaderboard", + }, + }, + MODEL_INFO: { + "metadata.co2_cost": { + group: "model_info", + size: COLUMN_SIZES.CO2_COST, + defaultVisible: true, + label: "CO₂ Cost (kg)", + }, + "metadata.hub_hearts": { + group: "model_info", + size: COLUMN_SIZES.HEARTS, + defaultVisible: false, + label: "Hub ❤️", + }, + "model.architecture": { + group: "model_info", + size: COLUMN_SIZES.ARCHITECTURE, + defaultVisible: false, + label: "Architecture", + }, + "model.precision": { + group: "model_info", + size: COLUMN_SIZES.PRECISION, + defaultVisible: false, + label: "Precision", + }, + "metadata.params_billions": { + group: "model_info", + size: COLUMN_SIZES.PARAMS, + defaultVisible: false, + label: "Parameters (B)", + }, + "metadata.hub_license": { + group: "model_info", + size: COLUMN_SIZES.LICENSE, + defaultVisible: false, + label: "License", + }, + "model.has_chat_template": { + group: "model_info", + size: COLUMN_SIZES.CHAT_TEMPLATE, + defaultVisible: false, + label: "Chat Template", + }, + }, + ADDITIONAL_INFO: { + "metadata.upload_date": { + group: "additional_info", + size: COLUMN_SIZES.UPLOAD_DATE, + defaultVisible: false, + label: "Upload Date", + }, + "metadata.submission_date": { + group: "additional_info", + size: COLUMN_SIZES.SUBMISSION_DATE, + defaultVisible: false, + label: "Submission Date", + }, + "metadata.generation": { + group: "additional_info", + size: COLUMN_SIZES.GENERATION, + defaultVisible: false, + label: "Generation", + }, + "metadata.base_model": { + group: "additional_info", + size: COLUMN_SIZES.BASE_MODEL, + defaultVisible: false, + label: "Base Model", + }, + "features.is_not_available_on_hub": { + group: "additional_info", + size: COLUMN_SIZES.HUB_AVAILABILITY, + defaultVisible: false, + label: "Hub Availability", + }, + "features.is_highlighted_by_maintainer": { + group: "additional_info", + size: COLUMN_SIZES.OFFICIAL_PROVIDER, + defaultVisible: false, + label: "Only Official Providers", + }, + "features.is_moe": { + group: "additional_info", + size: COLUMN_SIZES.MOE, + defaultVisible: false, + label: "Mixture of Experts", + }, + "features.is_flagged": { + group: "additional_info", + size: COLUMN_SIZES.FLAG_STATUS, + defaultVisible: false, + label: "Flag Status", + }, + }, +}; + +// Combine all columns for backward compatibility +const ALL_COLUMNS = { + ...COLUMNS.FIXED, + ...COLUMNS.EVALUATION, + ...COLUMNS.MODEL_INFO, + ...COLUMNS.ADDITIONAL_INFO, +}; + +// Column definitions for external use (maintaining the same interface) +const COLUMN_DEFINITIONS = { + ALL_COLUMNS, + COLUMN_GROUPS: { + "Evaluation Scores": Object.keys(COLUMNS.EVALUATION), + "Model Information": Object.keys(COLUMNS.MODEL_INFO), + "Additional Information": Object.keys(COLUMNS.ADDITIONAL_INFO), + }, + COLUMN_LABELS: Object.entries(ALL_COLUMNS).reduce((acc, [key, value]) => { + acc[key] = value.label; + return acc; + }, {}), + DEFAULT_VISIBLE: Object.entries(ALL_COLUMNS) + .filter(([_, value]) => value.defaultVisible) + .map(([key]) => key), + + // Remettre les getters nécessaires + get FIXED() { + return Object.entries(ALL_COLUMNS) + .filter(([_, def]) => def.group === "fixed") + .map(([key]) => key); + }, + + get EVALUATION() { + return Object.entries(ALL_COLUMNS) + .filter(([_, def]) => def.group === "evaluation") + .map(([key]) => key); + }, + + get OPTIONAL() { + return Object.entries(ALL_COLUMNS) + .filter(([_, def]) => def.group !== "fixed" && def.group !== "evaluation") + .map(([key]) => key); + }, + + get COLUMN_SIZES() { + return Object.entries(ALL_COLUMNS).reduce( + (acc, [key, def]) => ({ + ...acc, + [key]: def.size, + }), + {} + ); + }, +}; + +// Export constants maintaining the same interface +export const FILTER_PRECISIONS = FILTERS.PRECISIONS; +export const SUBMISSION_PRECISIONS = FILTERS.SUBMISSION_PRECISIONS; +export const PARAMS_RANGE = FILTERS.PARAMS_RANGE; +export const CACHE_SETTINGS = { DURATION: TIME.CACHE_DURATION }; +export const PINNED_MODELS = []; +export const DEBOUNCE_TIMINGS = TIME.DEBOUNCE; +export const ROW_SIZES = DISPLAY.ROW_SIZES; +export const SCORE_DISPLAY_OPTIONS = DISPLAY.SCORE_DISPLAY_OPTIONS; +export const RANKING_MODE_OPTIONS = DISPLAY.RANKING_MODE_OPTIONS; +export const BOOLEAN_FILTER_OPTIONS = FILTERS.BOOLEAN_OPTIONS; +export const HIGHLIGHT_FILTER_OPTIONS = FILTERS.HIGHLIGHT_OPTIONS; +export { COLUMN_DEFINITIONS }; + +// Export defaults for backward compatibility +export const TABLE_DEFAULTS = { + ROW_SIZE: "normal", + SCORE_DISPLAY: "normalized", + AVERAGE_MODE: "all", + RANKING_MODE: "static", + SEARCH: { + PRECISIONS: FILTERS.PRECISIONS, + TYPES: MODEL_TYPE_ORDER, + PARAMS_RANGE: FILTERS.PARAMS_RANGE, + }, + DEFAULT_SELECTED: { + searchValue: "", + selectedPrecisions: FILTERS.PRECISIONS, + selectedTypes: MODEL_TYPE_ORDER, + paramsRange: FILTERS.PARAMS_RANGE, + selectedBooleanFilters: [], + }, + DEBOUNCE: TIME.DEBOUNCE, + COLUMNS: COLUMN_DEFINITIONS, + PINNED_MODELS: [], + CACHE_DURATION: TIME.CACHE_DURATION, +}; + +// Highlight colors for search and table +export const HIGHLIGHT_COLORS = [ + "#1f77b4", // bleu + "#ff7f0e", // orange + "#2ca02c", // vert + "#d62728", // rouge + "#9467bd", // violet + "#8c564b", // marron + "#e377c2", // rose + "#7f7f7f", // gris + "#bcbd22", // olive + "#17becf", // cyan +]; + +// Skeleton columns widths (in pixels) +export const SKELETON_COLUMNS = [ + 40, // Checkbox + COLUMN_SIZES.RANK, // Rank + COLUMN_SIZES.TYPE_ICON, // Type icon + COLUMN_SIZES.MODEL, // Model name + COLUMN_SIZES.AVERAGE_SCORE, // Average score + COLUMN_SIZES.BENCHMARK, // Benchmark 1 + COLUMN_SIZES.BENCHMARK, // Benchmark 2 + COLUMN_SIZES.BENCHMARK, // Benchmark 3 + COLUMN_SIZES.BENCHMARK, // Benchmark 4 + COLUMN_SIZES.BENCHMARK, // Benchmark 5 + COLUMN_SIZES.BENCHMARK, // Benchmark 6 +]; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/modelTypes.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/modelTypes.js new file mode 100644 index 0000000000000000000000000000000000000000..46683b1e6d3a8b20e364260f579ce559a71e3e8b --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/modelTypes.js @@ -0,0 +1,79 @@ +export const MODEL_TYPE_ORDER = [ + 'pretrained', + 'continuously pretrained', + 'fine-tuned', + 'chat', + 'merge', + 'multimodal' +]; + +export const MODEL_TYPES = { + 'pretrained': { + icon: '🟢', + label: 'Pretrained', + description: 'Base models trained on raw text data using self-supervised learning objectives', + order: 0 + }, + 'continuously pretrained': { + icon: '🟩', + label: 'Continuously Pretrained', + description: 'Base models with extended pretraining on additional data while maintaining original architecture', + order: 1 + }, + 'fine-tuned': { + icon: '🔶', + label: 'Fine-tuned', + description: 'Models specialized through task-specific training on curated datasets', + order: 2 + }, + 'chat': { + icon: '💬', + label: 'Chat', + description: 'Models optimized for conversation using various techniques: RLHF, DPO, IFT, SFT', + order: 3 + }, + 'merge': { + icon: '🤝', + label: 'Merge', + description: 'Models created by combining weights from multiple models', + order: 4 + }, + 'multimodal': { + icon: '🌸', + label: 'Multimodal', + description: 'Models capable of processing multiple types of input', + order: 5 + } +}; + +export const getModelTypeIcon = (type) => { + const cleanType = type.toLowerCase().trim(); + const matchedType = Object.entries(MODEL_TYPES).find(([key]) => + cleanType.includes(key) + ); + return matchedType ? matchedType[1].icon : '❓'; +}; + +export const getModelTypeLabel = (type) => { + const cleanType = type.toLowerCase().trim(); + const matchedType = Object.entries(MODEL_TYPES).find(([key]) => + cleanType.includes(key) + ); + return matchedType ? matchedType[1].label : type; +}; + +export const getModelTypeDescription = (type) => { + const cleanType = type.toLowerCase().trim(); + const matchedType = Object.entries(MODEL_TYPES).find(([key]) => + cleanType.includes(key) + ); + return matchedType ? matchedType[1].description : 'Unknown model type'; +}; + +export const getModelTypeOrder = (type) => { + const cleanType = type.toLowerCase().trim(); + const matchedType = Object.entries(MODEL_TYPES).find(([key]) => + cleanType.includes(key) + ); + return matchedType ? matchedType[1].order : Infinity; +}; \ No newline at end of file diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/quickFilters.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/quickFilters.js new file mode 100644 index 0000000000000000000000000000000000000000..ba06fe58ed05c895f238fe5f726fbb13fa7bd53a --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/quickFilters.js @@ -0,0 +1,51 @@ +export const QUICK_FILTER_PRESETS = [ + { + id: 'edge_device', + label: 'For Edge Devices', + shortDescription: 'Tiny models: Up to 3B parameters', + description: 'Lightweight models optimized for edge devices with limited resources. Ideal for mobile deployment or edge computing environments.', + filters: { + paramsRange: [0, 3], + selectedBooleanFilters: ['is_for_edge_devices'] + } + }, + { + id: 'small_models', + label: 'For Consumers', + shortDescription: 'Smol-LMs: 3-7B parameters', + description: 'Lightweight models optimized for consumer hardware with up to one GPU. Ideal for private consumer hardware.', + filters: { + paramsRange: [3, 7], + selectedBooleanFilters: ['is_for_edge_devices'] + } + }, + { + id: 'medium_models', + label: 'Mid-range', + shortDescription: 'Medium-sized models: 7B-65B parameters', + description: 'Overall balance between performance and required resources.', + filters: { + paramsRange: [7, 65], + selectedBooleanFilters: [] + } + }, + { + id: 'large_models', + label: 'For the GPU-rich', + shortDescription: 'Large models: 65B+ parameters', + description: 'Large-scale models offering (in theory) the best performance but requiring significant resources. Require adapted infrastructure.', + filters: { + paramsRange: [65, 141], + selectedBooleanFilters: [] + } + }, + { + id: 'official_providers', + label: 'Only Official Providers', + shortDescription: 'Officially provided models', + description: 'Models that are officially provided and maintained by official creators or organizations.', + filters: { + selectedBooleanFilters: ['is_highlighted_by_maintainer'] + } + } +]; \ No newline at end of file diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/tooltips.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/tooltips.js new file mode 100644 index 0000000000000000000000000000000000000000..482a449fe0ea807e757ccbcf9466196914435740 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/constants/tooltips.js @@ -0,0 +1,350 @@ +import { Box, Typography } from "@mui/material"; + +const createTooltipContent = (title, items) => ( + + + {title} + + + {items.map(({ label, description, subItems }, index) => ( +
  • + + {label}: {description} + {subItems && ( + + {subItems.map((item, subIndex) => ( +
  • + + {item} + +
  • + ))} +
    + )} + + + ))} +
    +
    +); + +export const COLUMN_TOOLTIPS = { + AVERAGE: createTooltipContent("Average score across all benchmarks:", [ + { + label: "Calculation", + description: "Weighted average of normalized scores from all benchmarks", + subItems: [ + "Each benchmark is normalized to a 0-100 scale", + "All normalised benchmarks are then averaged together", + ], + }, + ]), + + MULTIFIN: createTooltipContent("Multilingual Financial NLP (MultiFin):", [ + { + label: "Purpose", + description: + "Tests model's ability to understand real-world financial article headlines", + subItems: ["Language Understanding", "Topic Classification"], + }, + { + label: "Scoring: ACC Norm", + description: "Was the correct choice selected among the options.", + }, + ]), + + QA: createTooltipContent("Greek Financial Question Answering (QA):", [ + { + label: "Purpose", + description: + "Tests model's ability to understand real-world Greek financial exam questions", + subItems: ["Language Understanding", "Question Answering"], + }, + { + label: "Scoring: ACC Norm", + description: "Was the correct choice selected among the options.", + }, + ]), + + FNS: createTooltipContent("Financial Narrative Summarization (FNS):", [ + { + label: "Purpose", + description: + "Tests model's ability to summarize real-world financial annual reports", + subItems: ["Language Understanding", "Abstractive Summarization"], + }, + { + label: "Scoring: Rouge1", + description: "Was the overlap of unigrams (each word) between the predicted and reference summaries.", + }, + ]), + + FinNum: createTooltipContent("Financial Number Entity Recognition (FinNum):", [ + { + label: "Purpose", + description: + "Tests model's ability to recognize numbers from financial texts", + subItems: ["Language Understanding", "Named Entity Recognition"], + }, + { + label: "Scoring: Rouge1", + description: "Was the overlap of unigrams (each word) between the predicted and reference summaries.", + }, + ]), + + FinText: createTooltipContent("Financial Named Entity Recognition (FinText):", [ + { + label: "Purpose", + description: + "Tests model's ability to recognize financial entities, i.e, person, location, and organization", + subItems: ["Language Understanding", "Named Entity Recognition"], + }, + { + label: "Scoring: Rouge1", + description: "Was the overlap of unigrams (each word) between the predicted and reference summaries.", + }, + ]), + + ARCHITECTURE: createTooltipContent("Model Architecture Information:", [ + { + label: "Definition", + description: "The fundamental structure and design of the model", + subItems: [ + "Pretrained: Foundational models, initially trained on large datasets without task-specific tuning, serving as a versatile base for further development.", + "Continuously Pretrained: Base models trained with a data mix evolving as the model is trained, with the addition of specialized data during the last training steps.", + "Fine-tuned: Base models, fine-tuned on specialised domain data (legal, medical, ...), and optimized for particular tasks.", + "Chat: Models fine-tuned with IFT, RLHF, DPO, and other techniques, to handle conversational contexts effectively.", + "Merged: Combining multiple models through weights averaging or similar methods.", + "Multimodal: Models which can handle several modalities (text & image/audio/video/...). We only evaluate the text capabilities.", + ], + }, + { + label: "Impact", + description: "How architecture affects model capabilities", + subItems: [ + "Base models are expected to perform less well on instruction following evaluations, like IFEval.", + "Fine-tuned and chat models can be more verbose and more chatty than base models.", + "Merged models tend to exhibit good performance on benchmarks, which do not translate to real-world situations.", + ], + }, + ]), + + PRECISION: createTooltipContent("Numerical Precision Format:", [ + { + label: "Overview", + description: + "Data format used to store model weights and perform computations", + subItems: [ + "bfloat16: Half precision (Brain Float format), good for stability", + "float16: Half precision", + "8bit/4bit: Quantized formats, for efficiency", + "GPTQ/AWQ: Quantized methods", + ], + }, + { + label: "Impact", + description: "How precision affects model deployment", + subItems: [ + "Higher precision = better accuracy but more memory usage", + "Lower precision = faster inference and smaller size", + "Trade-off between model quality and resource usage", + ], + }, + ]), + + FLAGS: createTooltipContent("Model Flags and Special Features:", [ + { + label: "Filters", + subItems: [ + "Mixture of Expert: Uses a MoE architecture", + "Merged models: Created by averaging other models", + "Contaminated: Flagged by users from the community for (possibly accidental) cheating", + "Unavailable: No longer on the hub (private, deleted) or missing a license tag", + ], + }, + { + label: "Purpose", + description: "Why do people want to hide these models?", + subItems: [ + "Mixture of Experts: These models can be too parameter heavy", + "Merged models: Performance on benchmarks tend to be inflated compared to real life usage", + "Contaminated: Performance on benchmarks is inflated and not reflecting real life usage", + ], + }, + ]), + + PARAMETERS: createTooltipContent("Model Parameters:", [ + { + label: "Measurement", + description: "Total number of trainable parameters in billions", + subItems: [ + "Indicates model capacity and complexity", + "Correlates with computational requirements", + "Influences memory usage and inference speed", + ], + }, + ]), + + LICENSE: createTooltipContent("Model License Information:", [ + { + label: "Importance", + description: "Legal terms governing model usage and distribution", + subItems: [ + "Commercial vs non-commercial use", + "Attribution requirements", + "Modification and redistribution rights", + "Liability and warranty terms", + ], + }, + ]), + + CO2_COST: createTooltipContent("Carbon Dioxide Emissions:", [ + { + label: "What is it?", + description: "CO₂ emissions of the model evaluation ", + subItems: [ + "Only focuses on model inference for our specific setup", + "Considers data center location and energy mix", + "Allows equivalent comparision of models on our use case", + ], + }, + { + label: "Why it matters", + description: "Environmental impact of AI model training", + subItems: [ + "Large models can have significant carbon footprints", + "Helps make informed choices about model selection", + ], + }, + { + label: "Learn more", + description: + "For detailed information about our CO₂ calculation methodology, visit:", + subItems: [ + + Carbon Emissions Documentation ↗ + , + ], + }, + ]), +}; + +export const UI_TOOLTIPS = { + COLUMN_SELECTOR: "Choose which columns to display in the table", + DISPLAY_OPTIONS: createTooltipContent("Table Display Options", [ + { + label: "Overview", + description: "Configure how the table displays data and information", + subItems: [ + "Row size and layout", + "Score display format", + "Ranking calculation", + "Average score computation", + ], + }, + ]), + SEARCH_BAR: createTooltipContent("Advanced Model Search", [ + { + label: "Name Search", + description: "Search directly by model name", + subItems: [ + "Supports regular expressions (e.g., ^mistral.*7b)", + "Case sensitive", + ], + }, + { + label: "Field Search", + description: "Use @field:value syntax for precise filtering", + subItems: [ + "@architecture:llama - Filter by architecture", + "@license:mit - Filter by license", + "@precision:float16 - Filter by precision", + "@type:chat - Filter by model type", + ], + }, + { + label: "Multiple Searches", + description: "Combine multiple criteria using semicolons", + subItems: [ + "meta @license:mit; @architecture:llama", + "^mistral.*7b; @precision:float16", + ], + }, + ]), + QUICK_FILTERS: createTooltipContent( + "Filter models based on their size and applicable hardware:", + [ + { + label: "Edge devices (Up to 3BB)", + description: + "Efficient models for edge devices, optimized for blazing fast inference.", + }, + { + label: "Smol Models (3B-7B)", + description: + "Efficient models for consumer hardware, optimized for fast inference.", + }, + { + label: "Mid-range models (7B-65B)", + description: + "A bit of everything here, with overall balanced performance and resource usage around 30B.", + }, + { + label: "GPU-rich models (65B+)", + description: + "State-of-the-art performance for complex tasks, requires significant computing power.", + }, + { + label: "Official Providers", + description: + "Models directly maintained by their original creators, ensuring reliability and up-to-date performance.", + }, + ] + ), + ROW_SIZE: { + title: "Row Size", + description: + "Adjust the height of table rows. Compact is ideal for viewing more data at once, while Large provides better readability and touch targets.", + }, + SCORE_DISPLAY: { + title: "Score Display", + description: + "Choose between normalized scores (0-100% scale for easy comparison) or raw scores (actual benchmark results). Normalized scores help compare performance across different benchmarks, while raw scores show actual benchmark outputs.", + }, + RANKING_MODE: { + title: "Ranking Mode", + description: + "Choose between static ranking (original position in the full leaderboard) or dynamic ranking (position based on current filters and sorting).", + }, + AVERAGE_SCORE: { + title: "Average Score Calculation", + description: + "Define how the average score is calculated. 'All Scores' uses all benchmarks, while 'Visible Only' calculates the average using only the visible benchmark columns.", + }, +}; + +export const getTooltipStyle = {}; + +export const TABLE_TOOLTIPS = { + HUB_LINK: (modelName) => `View ${modelName} on Hugging Face Hub`, + EVAL_RESULTS: (modelName) => + `View detailed evaluation results for ${modelName}`, + POSITION_CHANGE: (change) => + `${Math.abs(change)} position${Math.abs(change) > 1 ? "s" : ""} ${ + change > 0 ? "up" : "down" + }`, + METADATA: { + TYPE: (type) => type || "-", + ARCHITECTURE: (arch) => arch || "-", + PRECISION: (precision) => precision || "-", + LICENSE: (license) => license || "-", + UPLOAD_DATE: (date) => date || "-", + SUBMISSION_DATE: (date) => date || "-", + BASE_MODEL: (model) => model || "-", + }, +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext.js new file mode 100644 index 0000000000000000000000000000000000000000..df7dfb5cef8a84dc557344bc161f7d0f73a7fa99 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext.js @@ -0,0 +1,775 @@ +import React, { + createContext, + useContext, + useReducer, + useEffect, + useMemo, + useCallback, +} from "react"; +import { useSearchParams, useLocation } from "react-router-dom"; +import { MODEL_TYPE_ORDER } from "../constants/modelTypes"; +import { FILTER_PRECISIONS, TABLE_DEFAULTS } from "../constants/defaults"; + +// Create context +const LeaderboardContext = createContext(); + +// Define default filter values +const DEFAULT_FILTERS = { + search: "", + precisions: FILTER_PRECISIONS, + types: MODEL_TYPE_ORDER, + paramsRange: [-1, 140], + booleanFilters: [], + isOfficialProviderActive: false, +}; + +// Define default display values +const DEFAULT_DISPLAY = { + rowSize: TABLE_DEFAULTS.ROW_SIZE, + scoreDisplay: TABLE_DEFAULTS.SCORE_DISPLAY, + averageMode: TABLE_DEFAULTS.AVERAGE_MODE, + rankingMode: TABLE_DEFAULTS.RANKING_MODE, + visibleColumns: [ + 'isPinned', + 'rank', + 'model_type', + 'id', + 'model.average_score', + 'evaluations.vision_average', + 'evaluations.audio_average', + 'evaluations.english_average', + 'evaluations.chinese_average', + 'evaluations.japanese_average', + 'evaluations.spanish_average', + 'evaluations.greek_average', + 'evaluations.bilingual_average', + 'evaluations.multilingual_average' + ], +}; + +// Create initial counter structure +const createInitialCounts = () => { + const modelTypes = {}; + MODEL_TYPE_ORDER.forEach((type) => { + modelTypes[type] = 0; + }); + + const precisions = {}; + FILTER_PRECISIONS.forEach((precision) => { + precisions[precision] = 0; + }); + + return { + modelTypes, + precisions, + maintainersHighlight: 0, + mixtureOfExperts: 0, + flagged: 0, + merged: 0, + notOnHub: 0, + parameterRanges: { + edge: 0, + small: 0, + medium: 0, + large: 0, + }, + }; +}; + +// Define initial state +const initialState = { + models: [], + loading: true, + countsReady: false, + error: null, + filters: DEFAULT_FILTERS, + display: DEFAULT_DISPLAY, + filtersExpanded: false, + pinnedModels: [], + filterCounts: { + normal: createInitialCounts(), + officialOnly: createInitialCounts(), + }, +}; + +// Function to normalize parameter value +const normalizeParams = (params) => { + const numParams = Number(params); + if (isNaN(numParams)) return null; + return Math.round(numParams * 100) / 100; +}; + +// Function to check if a parameter count is within a range +const isInParamRange = (params, range) => { + if (range[0] === -1 && range[1] === 140) return true; + const normalizedParams = normalizeParams(params); + if (normalizedParams === null) return false; + return normalizedParams >= range[0] && normalizedParams < range[1]; +}; + +// Function to check if a model matches filter criteria +const modelMatchesFilters = (model, filters) => { + // Filter by precision + if ( + filters.precisions.length > 0 && + !filters.precisions.includes(model.model.precision) + ) { + return false; + } + + // Filter by type + if (filters.types.length > 0) { + const modelType = model.model.type?.toLowerCase().trim(); + if (!filters.types.some((type) => modelType?.includes(type))) { + return false; + } + } + + // Filter by parameters + const params = Number( + model.metadata?.params_billions || model.features?.params_billions + ); + if (!isInParamRange(params, filters.paramsRange)) return false; + + // Filter by search + if (filters.search) { + const searchLower = filters.search.toLowerCase(); + const modelName = model.model.name.toLowerCase(); + if (!modelName.includes(searchLower)) return false; + } + + // Boolean filters + if (filters.booleanFilters.length > 0) { + return filters.booleanFilters.every((filter) => { + const filterValue = typeof filter === "object" ? filter.value : filter; + + // Maintainer's Highlight keeps positive logic + if (filterValue === "is_highlighted_by_maintainer") { + return model.features[filterValue]; + } + + // For all other filters, invert the logic + if (filterValue === "is_not_available_on_hub") { + return model.features[filterValue]; + } + + return !model.features[filterValue]; + }); + } + + return true; +}; + +// Function to calculate filtered model counts +const calculateFilteredCounts = ( + allRows, + totalPinnedCount, + filters, + filteredCount +) => { + // If no table, use raw filteredCount + if (!allRows) { + return { + currentFilteredCount: + typeof filteredCount === "number" ? filteredCount : 0, + totalPinnedCount: totalPinnedCount || 0, + }; + } + + // 1. Total number of rows (models matching filters) + const totalFilteredCount = allRows.length; + + // 2. Number of pinned models that also match filters + // These models are already included in totalFilteredCount, so we need to subtract them + // to avoid counting them twice + const pinnedMatchingFilters = allRows.filter((row) => { + const model = row.original; + return model.isPinned && modelMatchesFilters(model, filters); + }).length; + + return { + // Subtract pinned models that match filters + // as they are already displayed separately with "+X" + currentFilteredCount: totalFilteredCount - pinnedMatchingFilters, + totalPinnedCount: totalPinnedCount || 0, + }; +}; + +// Function to calculate counters +const calculateModelCounts = (models) => { + const normalCounts = createInitialCounts(); + const officialOnlyCounts = createInitialCounts(); + + models.forEach((model) => { + const isOfficial = + model.features?.is_highlighted_by_maintainer || + model.metadata?.is_highlighted_by_maintainer; + const countsToUpdate = [normalCounts]; + + if (isOfficial) { + countsToUpdate.push(officialOnlyCounts); + } + + countsToUpdate.forEach((counts) => { + // Model type + if (model.model?.type) { + const cleanType = model.model.type.toLowerCase().trim(); + const matchedType = MODEL_TYPE_ORDER.find((key) => + cleanType.includes(key) + ); + if (matchedType) { + counts.modelTypes[matchedType]++; + } + } + + // Precision + if (model.model?.precision) { + counts.precisions[model.model.precision]++; + } + + // Boolean filters + if ( + model.features?.is_highlighted_by_maintainer || + model.metadata?.is_highlighted_by_maintainer + ) + counts.maintainersHighlight++; + if (model.features?.is_moe || model.metadata?.is_moe) + counts.mixtureOfExperts++; + if (model.features?.is_flagged || model.metadata?.is_flagged) + counts.flagged++; + if (model.features?.is_merged || model.metadata?.is_merged) + counts.merged++; + if ( + !( + model.features?.is_not_available_on_hub || + model.metadata?.is_not_available_on_hub + ) + ) + counts.notOnHub++; + + // Parameter ranges + const params = Number( + model.metadata?.params_billions || model.features?.params_billions + ); + if (!isNaN(params)) { + if (isInParamRange(params, [0, 3])) counts.parameterRanges.edge++; + if (isInParamRange(params, [3, 7])) counts.parameterRanges.small++; + if (isInParamRange(params, [7, 65])) counts.parameterRanges.medium++; + if (isInParamRange(params, [65, 141])) counts.parameterRanges.large++; + } + }); + }); + + return { + normal: normalCounts, + officialOnly: officialOnlyCounts, + }; +}; + +// Define reducer +const reducer = (state, action) => { + switch (action.type) { + case "SET_MODELS": + const newCounts = calculateModelCounts(action.payload); + return { + ...state, + models: action.payload, + filterCounts: newCounts, + countsReady: true, + loading: false, + }; + + case "SET_LOADING": + return { + ...state, + loading: action.payload, + ...(action.payload ? { countsReady: false } : {}), + }; + + case "SET_ERROR": + return { + ...state, + error: action.payload, + loading: false, + }; + + case "SET_FILTER": + return { + ...state, + filters: { + ...state.filters, + [action.key]: action.value, + }, + }; + + case "SET_DISPLAY_OPTION": + return { + ...state, + display: { + ...state.display, + [action.key]: action.value, + }, + }; + + case "TOGGLE_PINNED_MODEL": + const modelKey = action.payload; + const pinnedModels = [...state.pinnedModels]; + const modelIndex = pinnedModels.indexOf(modelKey); + + if (modelIndex === -1) { + pinnedModels.push(modelKey); + } else { + pinnedModels.splice(modelIndex, 1); + } + + return { + ...state, + pinnedModels, + }; + + case "SET_PINNED_MODELS": + return { + ...state, + pinnedModels: action.payload, + }; + + case "TOGGLE_FILTERS_EXPANDED": + return { + ...state, + filtersExpanded: !state.filtersExpanded, + }; + + case "TOGGLE_OFFICIAL_PROVIDER": + return { + ...state, + filters: { + ...state.filters, + isOfficialProviderActive: !state.filters.isOfficialProviderActive, + }, + }; + + case "RESET_FILTERS": + return { + ...state, + filters: DEFAULT_FILTERS, + }; + + case "RESET_ALL": + return { + ...state, + filters: DEFAULT_FILTERS, + display: DEFAULT_DISPLAY, + pinnedModels: [], + }; + + default: + return state; + } +}; + +// Provider component +const LeaderboardProvider = ({ children }) => { + const [state, dispatch] = useReducer(reducer, initialState); + const [searchParams, setSearchParams] = useSearchParams(); + const location = useLocation(); + + // Effect to load initial values from URL + useEffect(() => { + // Skip URL sync if we're resetting + if (location.state?.skipUrlSync) return; + + const loadFromUrl = () => { + // Load filters + const searchFromUrl = searchParams.get("search"); + if (searchFromUrl) { + dispatch({ type: "SET_FILTER", key: "search", value: searchFromUrl }); + } + + const paramsFromUrl = searchParams.get("params")?.split(",").map(Number); + if (paramsFromUrl?.length === 2) { + dispatch({ + type: "SET_FILTER", + key: "paramsRange", + value: paramsFromUrl, + }); + } + + const filtersFromUrl = + searchParams.get("filters")?.split(",").filter(Boolean) || []; + if (filtersFromUrl.length > 0) { + dispatch({ + type: "SET_FILTER", + key: "booleanFilters", + value: filtersFromUrl, + }); + } + + const precisionsFromUrl = searchParams + .get("precision") + ?.split(",") + .filter(Boolean); + if (precisionsFromUrl) { + dispatch({ + type: "SET_FILTER", + key: "precisions", + value: precisionsFromUrl, + }); + } + + const typesFromUrl = searchParams + .get("types") + ?.split(",") + .filter(Boolean); + if (typesFromUrl) { + dispatch({ type: "SET_FILTER", key: "types", value: typesFromUrl }); + } + + const officialFromUrl = searchParams.get("official") === "true"; + if (officialFromUrl) { + dispatch({ + type: "SET_FILTER", + key: "isOfficialProviderActive", + value: true, + }); + } + + // Load pinned models + const pinnedFromUrl = + searchParams.get("pinned")?.split(",").filter(Boolean) || []; + if (pinnedFromUrl.length > 0) { + dispatch({ type: "SET_PINNED_MODELS", payload: pinnedFromUrl }); + } + + // Load visible columns + const columnsFromUrl = searchParams + .get("columns") + ?.split(",") + .filter(Boolean); + if (columnsFromUrl) { + dispatch({ + type: "SET_DISPLAY_OPTION", + key: "visibleColumns", + value: columnsFromUrl, + }); + } + + // Load table options + const rowSizeFromUrl = searchParams.get("rowSize"); + if (rowSizeFromUrl) { + dispatch({ + type: "SET_DISPLAY_OPTION", + key: "rowSize", + value: rowSizeFromUrl, + }); + } + + const scoreDisplayFromUrl = searchParams.get("scoreDisplay"); + if (scoreDisplayFromUrl) { + dispatch({ + type: "SET_DISPLAY_OPTION", + key: "scoreDisplay", + value: scoreDisplayFromUrl, + }); + } + + const averageModeFromUrl = searchParams.get("averageMode"); + if (averageModeFromUrl) { + dispatch({ + type: "SET_DISPLAY_OPTION", + key: "averageMode", + value: averageModeFromUrl, + }); + } + + const rankingModeFromUrl = searchParams.get("rankingMode"); + if (rankingModeFromUrl) { + dispatch({ + type: "SET_DISPLAY_OPTION", + key: "rankingMode", + value: rankingModeFromUrl, + }); + } + }; + + loadFromUrl(); + }, [searchParams, location.state]); + + // Effect to synchronize filters with URL + useEffect(() => { + // Skip URL sync if we're resetting + if (location.state?.skipUrlSync) return; + + const newSearchParams = new URLSearchParams(searchParams); + const currentParams = searchParams.get("params")?.split(",").map(Number); + const currentFilters = + searchParams.get("filters")?.split(",").filter(Boolean) || []; + const currentSearch = searchParams.get("search"); + const currentPinned = + searchParams.get("pinned")?.split(",").filter(Boolean) || []; + const currentColumns = + searchParams.get("columns")?.split(",").filter(Boolean) || []; + const currentRowSize = searchParams.get("rowSize"); + const currentScoreDisplay = searchParams.get("scoreDisplay"); + const currentAverageMode = searchParams.get("averageMode"); + const currentRankingMode = searchParams.get("rankingMode"); + const currentOfficialProvider = searchParams.get("official") === "true"; + const currentPrecisions = + searchParams.get("precision")?.split(",").filter(Boolean) || []; + const currentTypes = + searchParams.get("types")?.split(",").filter(Boolean) || []; + + // Only update URL if values have changed + const paramsChanged = + !currentParams || + currentParams[0] !== state.filters.paramsRange[0] || + currentParams[1] !== state.filters.paramsRange[1]; + + const filtersChanged = + state.filters.booleanFilters.length !== currentFilters.length || + state.filters.booleanFilters.some((f) => !currentFilters.includes(f)); + + const searchChanged = state.filters.search !== currentSearch; + + const pinnedChanged = + state.pinnedModels.length !== currentPinned.length || + state.pinnedModels.some((m) => !currentPinned.includes(m)); + + const columnsChanged = + state.display.visibleColumns.length !== currentColumns.length || + state.display.visibleColumns.some((c) => !currentColumns.includes(c)); + + const rowSizeChanged = state.display.rowSize !== currentRowSize; + const scoreDisplayChanged = + state.display.scoreDisplay !== currentScoreDisplay; + const averageModeChanged = state.display.averageMode !== currentAverageMode; + const rankingModeChanged = state.display.rankingMode !== currentRankingMode; + const officialProviderChanged = + state.filters.isOfficialProviderActive !== currentOfficialProvider; + const precisionsChanged = + state.filters.precisions.length !== currentPrecisions.length || + state.filters.precisions.some((p) => !currentPrecisions.includes(p)); + const typesChanged = + state.filters.types.length !== currentTypes.length || + state.filters.types.some((t) => !currentTypes.includes(t)); + + if (paramsChanged) { + if ( + state.filters.paramsRange[0] !== -1 || + state.filters.paramsRange[1] !== 140 + ) { + newSearchParams.set("params", state.filters.paramsRange.join(",")); + } else { + newSearchParams.delete("params"); + } + } + + if (filtersChanged) { + if (state.filters.booleanFilters.length > 0) { + newSearchParams.set("filters", state.filters.booleanFilters.join(",")); + } else { + newSearchParams.delete("filters"); + } + } + + if (searchChanged) { + if (state.filters.search) { + newSearchParams.set("search", state.filters.search); + } else { + newSearchParams.delete("search"); + } + } + + if (pinnedChanged) { + if (state.pinnedModels.length > 0) { + newSearchParams.set("pinned", state.pinnedModels.join(",")); + } else { + newSearchParams.delete("pinned"); + } + } + + if (columnsChanged) { + if ( + JSON.stringify([...state.display.visibleColumns].sort()) !== + JSON.stringify([...TABLE_DEFAULTS.COLUMNS.DEFAULT_VISIBLE].sort()) + ) { + newSearchParams.set("columns", state.display.visibleColumns.join(",")); + } else { + newSearchParams.delete("columns"); + } + } + + if (rowSizeChanged) { + if (state.display.rowSize !== TABLE_DEFAULTS.ROW_SIZE) { + newSearchParams.set("rowSize", state.display.rowSize); + } else { + newSearchParams.delete("rowSize"); + } + } + + if (scoreDisplayChanged) { + if (state.display.scoreDisplay !== TABLE_DEFAULTS.SCORE_DISPLAY) { + newSearchParams.set("scoreDisplay", state.display.scoreDisplay); + } else { + newSearchParams.delete("scoreDisplay"); + } + } + + if (averageModeChanged) { + if (state.display.averageMode !== TABLE_DEFAULTS.AVERAGE_MODE) { + newSearchParams.set("averageMode", state.display.averageMode); + } else { + newSearchParams.delete("averageMode"); + } + } + + if (rankingModeChanged) { + if (state.display.rankingMode !== TABLE_DEFAULTS.RANKING_MODE) { + newSearchParams.set("rankingMode", state.display.rankingMode); + } else { + newSearchParams.delete("rankingMode"); + } + } + + if (officialProviderChanged) { + if (state.filters.isOfficialProviderActive) { + newSearchParams.set("official", "true"); + } else { + newSearchParams.delete("official"); + } + } + + if (precisionsChanged) { + if ( + JSON.stringify([...state.filters.precisions].sort()) !== + JSON.stringify([...FILTER_PRECISIONS].sort()) + ) { + newSearchParams.set("precision", state.filters.precisions.join(",")); + } else { + newSearchParams.delete("precision"); + } + } + + if (typesChanged) { + if ( + JSON.stringify([...state.filters.types].sort()) !== + JSON.stringify([...MODEL_TYPE_ORDER].sort()) + ) { + newSearchParams.set("types", state.filters.types.join(",")); + } else { + newSearchParams.delete("types"); + } + } + + if ( + paramsChanged || + filtersChanged || + searchChanged || + pinnedChanged || + columnsChanged || + rowSizeChanged || + scoreDisplayChanged || + averageModeChanged || + rankingModeChanged || + officialProviderChanged || + precisionsChanged || + typesChanged + ) { + // Update search params and let HashRouter handle the URL + setSearchParams(newSearchParams); + } + }, [state, searchParams, location.state]); + + const actions = useMemo( + () => ({ + setModels: (models) => dispatch({ type: "SET_MODELS", payload: models }), + setLoading: (loading) => + dispatch({ type: "SET_LOADING", payload: loading }), + setError: (error) => dispatch({ type: "SET_ERROR", payload: error }), + setFilter: (key, value) => dispatch({ type: "SET_FILTER", key, value }), + setDisplayOption: (key, value) => + dispatch({ type: "SET_DISPLAY_OPTION", key, value }), + togglePinnedModel: (modelKey) => + dispatch({ type: "TOGGLE_PINNED_MODEL", payload: modelKey }), + toggleOfficialProvider: () => + dispatch({ type: "TOGGLE_OFFICIAL_PROVIDER" }), + toggleFiltersExpanded: () => + dispatch({ type: "TOGGLE_FILTERS_EXPANDED" }), + resetFilters: () => { + dispatch({ type: "RESET_FILTERS" }); + const newParams = new URLSearchParams(searchParams); + [ + "filters", + "params", + "precision", + "types", + "official", + "search", + ].forEach((param) => { + newParams.delete(param); + }); + setSearchParams(newParams); + }, + resetAll: () => { + // Reset all state + dispatch({ type: "RESET_ALL" }); + // Clear all URL params with skipUrlSync flag + setSearchParams({}, { state: { skipUrlSync: true } }); + }, + }), + [searchParams, setSearchParams] + ); + + // Function to calculate counts (exposed via context) + const getFilteredCounts = useCallback( + (allRows, totalPinnedCount, filteredCount) => { + return calculateFilteredCounts( + allRows, + totalPinnedCount, + state.filters, + filteredCount + ); + }, + [state.filters] + ); + + // Also expose filtering function for reuse elsewhere + const checkModelMatchesFilters = useCallback( + (model) => { + return modelMatchesFilters(model, state.filters); + }, + [state.filters] + ); + + const value = useMemo( + () => ({ + state: { + ...state, + loading: state.loading || !state.countsReady, + }, + actions, + utils: { + getFilteredCounts, + checkModelMatchesFilters, + }, + }), + [state, actions, getFilteredCounts, checkModelMatchesFilters] + ); + + return ( + + {children} + + ); +}; + +// Hook to use context +const useLeaderboard = () => { + const context = useContext(LeaderboardContext); + if (!context) { + throw new Error("useLeaderboard must be used within a LeaderboardProvider"); + } + return context; +}; + +export { useLeaderboard }; +export default LeaderboardProvider; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useBatchedState.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useBatchedState.js new file mode 100644 index 0000000000000000000000000000000000000000..ad11c91393ca9e413853ae440154b948293103e9 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useBatchedState.js @@ -0,0 +1,31 @@ +import { useState, useCallback, useTransition } from 'react'; + +export const useBatchedState = (initialState, options = {}) => { + const { batchDelay = 0, useTransitions = false } = options; + const [state, setState] = useState(typeof initialState === 'function' ? initialState() : initialState); + const [isPending, startTransition] = useTransition(); + + const setBatchedState = useCallback((newState) => { + if (useTransitions) { + startTransition(() => { + if (batchDelay > 0) { + setTimeout(() => { + setState(newState); + }, batchDelay); + } else { + setState(newState); + } + }); + } else { + if (batchDelay > 0) { + setTimeout(() => { + setState(newState); + }, batchDelay); + } else { + setState(newState); + } + } + }, [batchDelay, useTransitions]); + + return [state, setBatchedState, isPending]; +}; \ No newline at end of file diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useDataUtils.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useDataUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..e58f9c78976d8d6867670d2729186e23d88d5570 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useDataUtils.js @@ -0,0 +1,267 @@ +import { useMemo } from "react"; +import { + looksLikeRegex, + parseSearchQuery, + getValueByPath, +} from "../utils/searchUtils"; +import { ALLOWED_MODELS, isModelAllowed } from "../constants/allowedModels"; + +// 硬编码数据集 +const HARDCODED_SCORES = { + vision: { + "GPT-4o": 55.54, "o3-Mini": 0.00, "Deepseek-V3": 0.00, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 16.27, + "meta-llama/Llama-3.1-70B-Instruct": 0.00, "google/gemma-3-4b-it": 14.97, "google/gemma-3-27b-it": 25.57, + "Qwen/Qwen2.5-32B-Instruct": 0.00, "Qwen/Qwen2.5-Omni-7B": 24.97, "TheFinAI/finma-7b-full": 0.00, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 0.00, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 0.00, + "TheFinAI/FinMA-ES-Bilingual": 0.00, "TheFinAI/plutus-8B-instruct": 0.00, "Qwen-VL-MAX": 18.47, + "LLaVA-1.6 Vicuna-13B": 19.77, "Deepseek-VL-7B-Chat": 19.10, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + audio: { + "GPT-4o": 55.56, "o3-Mini": 0.00, "Deepseek-V3": 0.00, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 0.00, + "meta-llama/Llama-3.1-70B-Instruct": 0.00, "google/gemma-3-4b-it": 0.00, "google/gemma-3-27b-it": 0.00, + "Qwen/Qwen2.5-32B-Instruct": 0.00, "Qwen/Qwen2.5-Omni-7B": 48.22, "TheFinAI/finma-7b-full": 0.00, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 0.00, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 0.00, + "TheFinAI/FinMA-ES-Bilingual": 0.00, "TheFinAI/plutus-8B-instruct": 0.00, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 51.58, "Qwen2-Audio-7B": 48.02, + "Qwen2-Audio-7B-Instruct": 50.06, "SALMONN-7B": 24.24, "SALMONN-13B": 24.59 + }, + english: { + "GPT-4o": 42.18, "o3-Mini": 20.20, "Deepseek-V3": 18.04, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 24.16, + "meta-llama/Llama-3.1-70B-Instruct": 38.71, "google/gemma-3-4b-it": 16.13, "google/gemma-3-27b-it": 17.19, + "Qwen/Qwen2.5-32B-Instruct": 32.01, "Qwen/Qwen2.5-Omni-7B": 24.99, "TheFinAI/finma-7b-full": 28.89, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 29.39, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 26.38, + "TheFinAI/FinMA-ES-Bilingual": 31.72, "TheFinAI/plutus-8B-instruct": 27.82, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + chinese: { + "GPT-4o": 60.34, "o3-Mini": 0.00, "Deepseek-V3": 60.94, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 64.51, + "meta-llama/Llama-3.1-70B-Instruct": 56.74, "google/gemma-3-4b-it": 26.23, "google/gemma-3-27b-it": 26.24, + "Qwen/Qwen2.5-32B-Instruct": 56.62, "Qwen/Qwen2.5-Omni-7B": 53.09, "TheFinAI/finma-7b-full": 24.42, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 23.04, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 13.18, + "TheFinAI/FinMA-ES-Bilingual": 21.50, "TheFinAI/plutus-8B-instruct": 31.04, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + japanese: { + "GPT-4o": 0.00, "o3-Mini": 0.00, "Deepseek-V3": 0.00, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 48.43, + "meta-llama/Llama-3.1-70B-Instruct": 32.17, "google/gemma-3-4b-it": 8.98, "google/gemma-3-27b-it": 23.96, + "Qwen/Qwen2.5-32B-Instruct": 4.54, "Qwen/Qwen2.5-Omni-7B": 44.35, "TheFinAI/finma-7b-full": 46.94, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 47.59, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 23.96, + "TheFinAI/FinMA-ES-Bilingual": 57.36, "TheFinAI/plutus-8B-instruct": 34.62, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + spanish: { + "GPT-4o": 29.80, "o3-Mini": 4.53, "Deepseek-V3": 25.49, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 47.90, + "meta-llama/Llama-3.1-70B-Instruct": 37.84, "google/gemma-3-4b-it": 27.66, "google/gemma-3-27b-it": 27.77, + "Qwen/Qwen2.5-32B-Instruct": 37.47, "Qwen/Qwen2.5-Omni-7B": 39.16, "TheFinAI/finma-7b-full": 27.04, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 42.86, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 28.01, + "TheFinAI/FinMA-ES-Bilingual": 38.69, "TheFinAI/plutus-8B-instruct": 40.16, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + greek: { + "GPT-4o": 43.04, "o3-Mini": 9.48, "Deepseek-V3": 39.07, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 48.95, + "meta-llama/Llama-3.1-70B-Instruct": 43.60, "google/gemma-3-4b-it": 15.45, "google/gemma-3-27b-it": 15.44, + "Qwen/Qwen2.5-32B-Instruct": 44.32, "Qwen/Qwen2.5-Omni-7B": 23.45, "TheFinAI/finma-7b-full": 17.93, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 29.49, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 20.91, + "TheFinAI/FinMA-ES-Bilingual": 15.47, "TheFinAI/plutus-8B-instruct": 60.19, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + bilingual: { + "GPT-4o": 92.29, "o3-Mini": 90.13, "Deepseek-V3": 86.26, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 89.17, + "meta-llama/Llama-3.1-70B-Instruct": 92.13, "google/gemma-3-4b-it": 35.92, "google/gemma-3-27b-it": 35.92, + "Qwen/Qwen2.5-32B-Instruct": 92.29, "Qwen/Qwen2.5-Omni-7B": 91.80, "TheFinAI/finma-7b-full": 69.24, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 91.60, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 71.81, + "TheFinAI/FinMA-ES-Bilingual": 66.57, "TheFinAI/plutus-8B-instruct": 91.59, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + }, + multilingual: { + "GPT-4o": 6.53, "o3-Mini": 7.80, "Deepseek-V3": 36.99, "meta-llama/Llama-4-Scout-17B-16E-Instruct": 13.52, + "meta-llama/Llama-3.1-70B-Instruct": 21.97, "google/gemma-3-4b-it": 0.00, "google/gemma-3-27b-it": 0.00, + "Qwen/Qwen2.5-32B-Instruct": 18.48, "Qwen/Qwen2.5-Omni-7B": 16.29, "TheFinAI/finma-7b-full": 3.10, + "Duxiaoman-DI/Llama3.1-XuanYuan-FinX1-Preview": 1.76, "cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese": 10.25, + "TheFinAI/FinMA-ES-Bilingual": 0.35, "TheFinAI/plutus-8B-instruct": 7.24, "Qwen-VL-MAX": 0.00, + "LLaVA-1.6 Vicuna-13B": 0.00, "Deepseek-VL-7B-Chat": 0.00, "Whisper-V3": 0.00, "Qwen2-Audio-7B": 0.00, + "Qwen2-Audio-7B-Instruct": 0.00, "SALMONN-7B": 0.00, "SALMONN-13B": 0.00 + } +}; + +// Calculate min/max averages +export const useAverageRange = (data) => { + return useMemo(() => { + const averages = data.map((item) => item.model.average_score); + return { + minAverage: Math.min(...averages), + maxAverage: Math.max(...averages), + }; + }, [data]); +}; + +// Generate colors for scores +export const useColorGenerator = (minAverage, maxAverage) => { + return useMemo(() => { + const colorCache = new Map(); + return (value) => { + const cached = colorCache.get(value); + if (cached) return cached; + + const normalizedValue = (value - minAverage) / (maxAverage - minAverage); + const red = Math.round(255 * (1 - normalizedValue) * 1); + const green = Math.round(255 * normalizedValue) * 1; + // const color = `rgba(${red}, ${green}, 0, 1)`; + const color = `rgba(${red}, 0, ${green}, 1)`; + colorCache.set(value, color); + return color; + }; + }, [minAverage, maxAverage]); +}; + +// Process data with boolean standardization +export const useProcessedData = (data, averageMode, visibleColumns) => { + return useMemo(() => { + // 直接使用硬编码数据创建模型列表 + const modelList = []; + + // 从HARDCODED_SCORES中获取所有模型名称 + const modelNames = new Set(); + Object.values(HARDCODED_SCORES).forEach(categoryData => { + Object.entries(categoryData).forEach(([modelName, score]) => { + // 添加所有模型,不管分数是否为0 + modelNames.add(modelName); + }); + }); + + // 为每个模型创建条目 + Array.from(modelNames).forEach((modelName, index) => { + // 创建硬编码评估数据 + const hardcodedEvaluations = { + vision_average: getHardcodedScore(modelName, 'vision'), + audio_average: getHardcodedScore(modelName, 'audio'), + english_average: getHardcodedScore(modelName, 'english'), + chinese_average: getHardcodedScore(modelName, 'chinese'), + japanese_average: getHardcodedScore(modelName, 'japanese'), + spanish_average: getHardcodedScore(modelName, 'spanish'), + greek_average: getHardcodedScore(modelName, 'greek'), + bilingual_average: getHardcodedScore(modelName, 'bilingual'), + multilingual_average: getHardcodedScore(modelName, 'multilingual') + }; + + // 计算总平均分(包含分数为0的类别) + const scores = Object.values(hardcodedEvaluations).filter(score => score !== null); + const averageScore = scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : null; + + // 创建模型数据 + modelList.push({ + id: `model-${index}`, + model: { + name: modelName, + average_score: averageScore, + type: "chat", // 统一设为chat类型 + }, + evaluations: hardcodedEvaluations, + features: { + is_moe: false, + is_flagged: false, + is_highlighted_by_maintainer: false, + is_merged: false, + is_not_available_on_hub: false, + }, + metadata: { + submission_date: new Date().toISOString(), + }, + isMissing: false, + }); + }); + + // 根据平均分排序 + modelList.sort((a, b) => { + if (a.model.average_score === null && b.model.average_score === null) + return 0; + if (a.model.average_score === null) return 1; + if (b.model.average_score === null) return -1; + return b.model.average_score - a.model.average_score; + }); + + // 添加排名 + return modelList.map((item, index) => ({ + ...item, + static_rank: index + 1, + })); + }, [data, averageMode, visibleColumns]); +}; + +// 辅助函数:从硬编码数据中获取分数 +function getHardcodedScore(modelName, category) { + if (!HARDCODED_SCORES[category]) return null; + + // 尝试精确匹配 + if (HARDCODED_SCORES[category][modelName] !== undefined) { + return HARDCODED_SCORES[category][modelName]; + } + + // 尝试部分匹配 + for (const key in HARDCODED_SCORES[category]) { + if (modelName.includes(key) || key.includes(modelName)) { + return HARDCODED_SCORES[category][key]; + } + } + + return null; +} + +// Common filtering logic +export const useFilteredData = ( + processedData, + selectedPrecisions, + selectedTypes, + paramsRange, + searchValue, + selectedBooleanFilters, + rankingMode, + pinnedModels = [], + isOfficialProviderActive = false +) => { + return useMemo(() => { + // 由于使用的是硬编码数据,这里直接返回所有数据而不进行过滤 + return processedData.map((item, index) => ({ + ...item, + dynamic_rank: index + 1, + rank: rankingMode === "static" ? item.static_rank : index + 1, + isPinned: pinnedModels.includes(item.id), + })); + }, [ + processedData, + rankingMode, + pinnedModels, + ]); +}; + +// Column visibility management +export const useColumnVisibility = (visibleColumns = []) => { + // Create secure visibility object + const columnVisibility = useMemo(() => { + // Check visible columns + const safeVisibleColumns = Array.isArray(visibleColumns) + ? visibleColumns + : []; + + const visibility = {}; + try { + safeVisibleColumns.forEach((columnKey) => { + if (typeof columnKey === "string") { + visibility[columnKey] = true; + } + }); + } catch (error) { + console.warn("Error in useColumnVisibility:", error); + } + return visibility; + }, [visibleColumns]); + + return columnVisibility; +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useLeaderboardData.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useLeaderboardData.js new file mode 100644 index 0000000000000000000000000000000000000000..dd63c69dd2b253c21852438618e285ef2615a84f --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/hooks/useLeaderboardData.js @@ -0,0 +1,133 @@ +import { useMemo, useRef, useState } from "react"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useSearchParams } from "react-router-dom"; +import { useLeaderboard } from "../context/LeaderboardContext"; +import { useDataProcessing } from "../components/Table/hooks/useDataProcessing"; + +const CACHE_KEY = "leaderboardData"; +const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes + +export const useLeaderboardData = () => { + const queryClient = useQueryClient(); + const [searchParams] = useSearchParams(); + const isInitialLoadRef = useRef(true); + + const { data, isLoading, error } = useQuery({ + queryKey: ["leaderboard"], + queryFn: async () => { + try { + const cachedData = localStorage.getItem(CACHE_KEY); + if (cachedData) { + const { data: cached, timestamp } = JSON.parse(cachedData); + const age = Date.now() - timestamp; + if (age < CACHE_DURATION) { + return cached; + } + } + + const response = await fetch("/api/leaderboard/formatted"); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const newData = await response.json(); + localStorage.setItem( + CACHE_KEY, + JSON.stringify({ + data: newData, + timestamp: Date.now(), + }) + ); + + return newData; + } catch (error) { + console.error("Detailed error:", error); + throw error; + } + }, + staleTime: CACHE_DURATION, + cacheTime: CACHE_DURATION * 2, + refetchOnWindowFocus: false, + enabled: isInitialLoadRef.current || !!searchParams.toString(), + }); + + useMemo(() => { + if (data && isInitialLoadRef.current) { + isInitialLoadRef.current = false; + } + }, [data]); + + return { + data, + isLoading, + error, + refetch: () => queryClient.invalidateQueries(["leaderboard"]), + }; +}; + +export const useLeaderboardProcessing = () => { + const { state, actions } = useLeaderboard(); + const [sorting, setSorting] = useState([ + { id: "model.average_score", desc: true }, + ]); + + const memoizedData = useMemo(() => state.models, [state.models]); + const memoizedFilters = useMemo( + () => ({ + search: state.filters.search, + precisions: state.filters.precisions, + types: state.filters.types, + paramsRange: state.filters.paramsRange, + booleanFilters: state.filters.booleanFilters, + isOfficialProviderActive: state.filters.isOfficialProviderActive, + }), + [ + state.filters.search, + state.filters.precisions, + state.filters.types, + state.filters.paramsRange, + state.filters.booleanFilters, + state.filters.isOfficialProviderActive, + ] + ); + + const { + table, + minAverage, + maxAverage, + getColorForValue, + processedData, + filteredData, + columns, + columnVisibility, + } = useDataProcessing( + memoizedData, + memoizedFilters.search, + memoizedFilters.precisions, + memoizedFilters.types, + memoizedFilters.paramsRange, + memoizedFilters.booleanFilters, + sorting, + state.display.rankingMode, + state.display.averageMode, + state.display.visibleColumns, + state.display.scoreDisplay, + state.pinnedModels, + actions.togglePinnedModel, + setSorting, + memoizedFilters.isOfficialProviderActive + ); + + return { + table, + minAverage, + maxAverage, + getColorForValue, + processedData, + filteredData, + columns, + columnVisibility, + loading: state.loading, + error: state.error, + }; +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/styles/common.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/styles/common.js new file mode 100644 index 0000000000000000000000000000000000000000..06648e526979fd7c992ea3f3721468e261448593 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/styles/common.js @@ -0,0 +1,153 @@ +import { alpha } from "@mui/material"; + +export const commonStyles = { + // Tooltips + tooltip: { + sx: { + bgcolor: "background.tooltip", + "& .MuiTooltip-arrow": { + color: "background.tooltip", + }, + padding: "12px 16px", + maxWidth: 300, + fontSize: "0.875rem", + lineHeight: 1.4, + }, + }, + + // Progress bars + progressBar: { + position: "absolute", + left: -16, + top: -8, + height: "calc(100% + 16px)", + opacity: (theme) => (theme.palette.mode === "light" ? 0.1 : 0.2), + transition: "width 0.3s ease", + zIndex: 0, + }, + + // Cell containers + cellContainer: { + display: "flex", + alignItems: "center", + height: "100%", + width: "100%", + position: "relative", + }, + + // Hover effects + hoverEffect: (theme, isActive = false) => ({ + backgroundColor: isActive + ? alpha( + theme.palette.primary.main, + theme.palette.mode === "light" ? 0.08 : 0.16 + ) + : theme.palette.action.hover, + "& .MuiTypography-root": { + color: isActive ? "primary.main" : "text.primary", + }, + "& .MuiSvgIcon-root": { + color: isActive ? "primary.main" : "text.primary", + }, + }), + + // Filter groups + filterGroup: { + title: { + mb: 1, + fontSize: "0.8rem", + fontWeight: 700, + color: "text.primary", + display: "flex", + alignItems: "center", + gap: 0.5, + }, + container: { + display: "flex", + flexWrap: "wrap", + gap: 0.5, + alignItems: "center", + }, + }, + + // Option buttons (like in DisplayOptions) + optionButton: { + display: "flex", + alignItems: "center", + gap: 0.8, + cursor: "pointer", + padding: "4px 10px", + borderRadius: 1, + height: "32px", + "& .MuiSvgIcon-root": { + fontSize: "0.9rem", + }, + "& .MuiTypography-root": { + fontSize: "0.85rem", + }, + }, + + // Score indicators + scoreIndicator: { + dot: { + width: 10, + height: 10, + borderRadius: "50%", + marginLeft: -1, + }, + bar: { + position: "absolute", + left: -16, + top: -8, + height: "calc(100% + 16px)", + opacity: (theme) => (theme.palette.mode === "light" ? 0.1 : 0.2), + transition: "width 0.3s ease", + }, + }, + + // Popover content + popoverContent: { + p: 3, + width: 280, + maxHeight: 400, + overflowY: "auto", + }, +}; + +// Composant styles +export const componentStyles = { + // Table header cell + headerCell: { + borderRight: (theme) => + `1px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.05 : 0.1 + )}`, + "&:last-child": { + borderRight: "none", + }, + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + padding: "8px 16px", + backgroundColor: (theme) => theme.palette.background.paper, + position: "sticky !important", + top: 0, + zIndex: 10, + }, + + // Table cell + tableCell: { + borderRight: (theme) => + `1px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.05 : 0.1 + )}`, + "&:last-child": { + borderRight: "none", + }, + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + }, +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/utils/columnUtils.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/utils/columnUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..905f3ff547d1ff4e22c19e0e40e0ea0eadf81612 --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/utils/columnUtils.js @@ -0,0 +1,1434 @@ +import React from "react"; +import { Box, Typography, Link, Tooltip, IconButton } from "@mui/material"; +import { getModelTypeIcon } from "../constants/modelTypes"; +import TrendingUpIcon from "@mui/icons-material/TrendingUp"; +import TrendingDownIcon from "@mui/icons-material/TrendingDown"; +import RemoveIcon from "@mui/icons-material/Remove"; +import PushPinIcon from "@mui/icons-material/PushPin"; +import PushPinOutlinedIcon from "@mui/icons-material/PushPinOutlined"; +import { TABLE_DEFAULTS, HIGHLIGHT_COLORS } from "../constants/defaults"; +import { looksLikeRegex, extractTextSearch } from "./searchUtils"; +import { commonStyles } from "../styles/common"; +import { typeColumnSort } from "../components/Table/hooks/useSorting"; +import { + COLUMN_TOOLTIPS, + getTooltipStyle, + TABLE_TOOLTIPS, +} from "../constants/tooltips"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; +import { alpha } from "@mui/material/styles"; +import InfoIconWithTooltip from "../../../../../components/shared/InfoIconWithTooltip"; + +const DatabaseIcon = () => ( + +); + +const HighlightedText = ({ text, searchValue }) => { + if (!searchValue) return text; + + const searches = searchValue + .split(";") + .map((s) => s.trim()) + .filter(Boolean); + let result = text; + let fragments = [{ text: result, isMatch: false }]; + + searches.forEach((search, searchIndex) => { + if (!search) return; + + try { + let regex; + if (looksLikeRegex(search)) { + regex = new RegExp(search, "gi"); + } else { + regex = new RegExp(search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi"); + } + + const newFragments = []; + fragments.forEach((fragment) => { + if (fragment.isMatch) { + newFragments.push(fragment); + return; + } + + const parts = fragment.text.split(regex); + const matches = fragment.text.match(regex); + + if (!matches) { + newFragments.push(fragment); + return; + } + + parts.forEach((part, i) => { + if (part) newFragments.push({ text: part, isMatch: false }); + if (i < parts.length - 1) { + newFragments.push({ + text: matches[i], + isMatch: true, + colorIndex: searchIndex % HIGHLIGHT_COLORS.length, + }); + } + }); + }); + + fragments = newFragments; + } catch (e) { + console.warn("Invalid regex:", search); + } + }); + + return ( + <> + {fragments.map((fragment, i) => + fragment.isMatch ? ( + + theme.palette.getContrastText( + HIGHLIGHT_COLORS[fragment.colorIndex] + ), + fontWeight: 500, + px: 0.5, + py: "2px", + borderRadius: "3px", + mx: "1px", + overflow: "visible", + display: "inline-block", + }} + > + {fragment.text} + + ) : ( + {fragment.text} + ) + )} + + ); +}; + +const MEDAL_STYLES = { + 1: { + color: "#B58A1B", + background: "linear-gradient(135deg, #FFF7E0 0%, #FFD700 100%)", + borderColor: "rgba(212, 160, 23, 0.35)", + shadowColor: "rgba(212, 160, 23, 0.8)", + }, + 2: { + color: "#667380", + background: "linear-gradient(135deg, #FFFFFF 0%, #D8E3ED 100%)", + borderColor: "rgba(124, 139, 153, 0.35)", + shadowColor: "rgba(124, 139, 153, 0.8)", + }, + 3: { + color: "#B85C2F", + background: "linear-gradient(135deg, #FDF0E9 0%, #FFBC8C 100%)", + borderColor: "rgba(204, 108, 61, 0.35)", + shadowColor: "rgba(204, 108, 61, 0.8)", + }, +}; + +const getMedalStyle = (rank) => { + if (rank <= 3) { + const medalStyle = MEDAL_STYLES[rank]; + return { + color: medalStyle.color, + fontWeight: 900, + fontStretch: "150%", + fontFamily: '"Inter", -apple-system, sans-serif', + width: "24px", + height: "24px", + background: medalStyle.background, + border: "1px solid", + borderColor: medalStyle.borderColor, + borderRadius: "50%", + display: "flex", + alignItems: "center", + justifyContent: "center", + fontSize: "0.95rem", + lineHeight: 1, + padding: 0, + boxShadow: `1px 1px 0 ${medalStyle.shadowColor}`, + position: "relative", + }; + } + return { + color: "inherit", + fontWeight: rank <= 10 ? 600 : 400, + }; +}; + +const getRankStyle = (rank) => getMedalStyle(rank); + +const RankIndicator = ({ rank, previousRank, mode }) => { + const rankChange = previousRank ? previousRank - rank : 0; + + const RankChangeIndicator = ({ change }) => { + if (!change || mode === "dynamic") return null; + + const getChangeColor = (change) => { + if (change > 0) return "success.main"; + if (change < 0) return "error.main"; + return "grey.500"; + }; + + const getChangeIcon = (change) => { + if (change > 0) return ; + if (change < 0) return ; + return ; + }; + + return ( + 1 ? "s" : "" + } ${change > 0 ? "up" : "down"}`} + arrow + placement="right" + > + + {getChangeIcon(change)} + + + ); + }; + + return ( + + + {rank <= 3 ? ( + <> + + {rank} + + + + ) : ( + <> + + {rank} + + + + )} + + + ); +}; + +const getDetailsUrl = (modelName) => { + const formattedName = modelName.replace("/", "__"); + return `https://huggingface.co/datasets/TheFinAI/lm-eval-results-private`; +}; + +const HeaderLabel = ({ label, tooltip, className, isSorted }) => ( + + + {label} + + +); + +const InfoIcon = ({ tooltip }) => ( + + + +); + +const createHeaderCell = (label, tooltip) => (header) => + ( + + + + + {tooltip && } + + + ); + +const createModelHeader = + (totalModels, officialProvidersCount = 0, isOfficialProviderActive = false) => + ({ table }) => { + return ( + + + + Model + + + + ); + }; + +const BooleanValue = ({ value }) => { + if (value === null || value === undefined) + return -; + + return ( + ({ + display: "flex", + alignItems: "center", + justifyContent: "center", + borderRadius: "4px", + px: 1, + py: 0.5, + backgroundColor: value + ? theme.palette.mode === "dark" + ? alpha(theme.palette.success.main, 0.1) + : alpha(theme.palette.success.main, 0.1) + : theme.palette.mode === "dark" + ? alpha(theme.palette.error.main, 0.1) + : alpha(theme.palette.error.main, 0.1), + })} + > + ({ + color: value + ? theme.palette.mode === "dark" + ? theme.palette.success.light + : theme.palette.success.dark + : theme.palette.mode === "dark" + ? theme.palette.error.light + : theme.palette.error.dark, + })} + > + {value ? "Yes" : "No"} + + + ); +}; + +// 为Greek Financial LLM Leaderboard创建自定义标题组件 +const createGreekLeaderboardHeader = (header) => ( + + + + + + + + + + +); + +// 为各种类型的Leaderboard创建自定义标题组件 +const createLeaderboardHeader = (label, tooltip, linkUrl) => (header) => ( + + + + + + {linkUrl && ( + + + + )} + + +); + +export const createColumns = ( + getColorForValue, + scoreDisplay = "normalized", + columnVisibility = {}, + totalModels, + averageMode = "all", + searchValue = "", + rankingMode = "static", + onTogglePin, + hasPinnedRows = false +) => { + // Adjust column sizes based on the presence of pinned rows + const getColumnSize = (defaultSize) => + hasPinnedRows ? "auto" : `${defaultSize}px`; + + const baseColumns = [ + { + accessorKey: "isPinned", + header: () => null, + cell: ({ row }) => ( + + { + e.stopPropagation(); + e.preventDefault(); + onTogglePin(row.original.id); + }} + sx={{ + padding: 0.5, + color: row.original.isPinned ? "primary.main" : "grey.400", + "&:hover": { + color: "primary.main", + }, + }} + > + {row.original.isPinned ? ( + + ) : ( + + )} + + + ), + enableSorting: false, + size: getColumnSize(40), + }, + { + accessorKey: "rank", + header: createHeaderCell("Rank"), + cell: ({ row }) => { + const rank = + rankingMode === "static" + ? row.original.static_rank + : row.original.dynamic_rank; + const isMissing = row.original.isMissing === true; + + // Don't display rank for missing models + if (isMissing) { + return ( + + + - + + + ); + } + + return ( + + ); + }, + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["rank"], + }, + { + id: "model_type", + accessorFn: (row) => row.model.type, + header: createHeaderCell("Type"), + sortingFn: typeColumnSort, + cell: ({ row }) => { + const isMissing = row.original.isMissing === true; + + // Don't display type icon for missing models + if (isMissing) { + return ( + + + - + + + ); + } + + return ( + + + + {getModelTypeIcon(row.original.model.type)} + + + + ); + }, + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.type_icon"], + }, + { + accessorKey: "id", + header: createModelHeader(totalModels), + cell: ({ row }) => { + const textSearch = extractTextSearch(searchValue); + const modelName = row.original.model.name; + const isMissing = row.original.isMissing === true; + + // Display special style for missing models + if (isMissing) { + return ( + + + {modelName} + + + ); + } + + return ( + + + + theme.palette.mode === "dark" + ? theme.palette.info.light + : theme.palette.info.dark, + "& svg": { + opacity: 0.8, + }, + }, + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + flex: 1, + minWidth: 0, + fontWeight: row.original.static_rank <= 3 ? 600 : "inherit", + }} + > + + + + + + + + + ); + }, + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["id"], + }, + { + accessorKey: "model.average_score", + header: createHeaderCell("Average", COLUMN_TOOLTIPS.AVERAGE), + cell: ({ row, getValue }) => + createScoreCell(getValue, row, "model.average_score"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"], + meta: { + headerStyle: { + borderLeft: (theme) => + `2px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + )}`, + borderRight: (theme) => + `2px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + )}`, + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + borderLeft: (theme) => + `2px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + )}`, + borderRight: (theme) => + `2px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + )}`, + }), + }, + }, + { + accessorKey: "model.openness", + header: createHeaderCell("Openness", "Model openness classification"), + cell: ({ row }) => { + return ( + + + Class III-Open Model + + + ); + }, + size: 150, + enableSorting: false, + meta: { + headerStyle: { + borderRight: (theme) => + `2px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + )}`, + }, + cellStyle: () => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + borderRight: (theme) => + `2px solid ${alpha( + theme.palette.divider, + theme.palette.mode === "dark" ? 0.1 : 0.2 + )}`, + }), + }, + }, + ]; + const createScoreCell = (getValue, row, field) => { + const value = getValue(); + const rawValue = field.includes("normalized") + ? row.original.evaluations[field.split(".")[1]]?.value + : value; + + const isAverageColumn = field === "model.average_score"; + const hasNoValue = value === null || value === undefined; + const isMissing = row.original.isMissing === true; + + // Display special text for "missing" models + if (isMissing) { + return ( + + + + missing + + + + ); + } + + return ( + + {!hasNoValue && (scoreDisplay === "normalized" || isAverageColumn) && ( + (theme.palette.mode === "light" ? 0.1 : 0.2), + transition: "width 0.3s ease", + zIndex: 0, + }} + /> + )} + + {isAverageColumn && !hasNoValue && ( + + )} + + {hasNoValue ? ( + "-" + ) : ( + <> + {isAverageColumn ? ( + <> + {value.toFixed(2)} + % + + ) : scoreDisplay === "normalized" ? ( + <> + {value.toFixed(2)} + % + + ) : ( + <>{rawValue.toFixed(2)} + )} + + )} + + + + ); + }; + + const evaluationColumns = [ + { + accessorKey: "evaluations.greek_average", + header: createGreekLeaderboardHeader, + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.greek_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.info.light, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.info.light, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.vision_average", + header: createLeaderboardHeader("Vision", "Average performance on vision tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.vision_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.primary.light, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.primary.light, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.audio_average", + header: createLeaderboardHeader("Audio", "Average performance on audio tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.audio_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.secondary.light, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.secondary.light, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.english_average", + header: createLeaderboardHeader("English", "Average performance on English language tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.english_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.success.light, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.success.light, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.chinese_average", + header: createLeaderboardHeader("Chinese", "Average performance on Chinese language tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.chinese_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.warning.light, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.warning.light, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.japanese_average", + header: createLeaderboardHeader("Japanese", "Average performance on Japanese language tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.japanese_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.error.light, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.error.light, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.spanish_average", + header: createLeaderboardHeader("Spanish", "Average performance on Spanish language tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.spanish_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.info.main, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.info.main, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.bilingual_average", + header: createLeaderboardHeader("Bilingual", "Average performance on bilingual tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.bilingual_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.primary.main, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.primary.main, 0.05), + }), + }, + }, + { + accessorKey: "evaluations.multilingual_average", + header: createLeaderboardHeader("Multilingual", "Average performance on multilingual tasks", null), + cell: ({ row, getValue }) => createScoreCell(getValue, row, "evaluations.multilingual_average"), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.average_score"] || 100, + meta: { + headerStyle: { + backgroundColor: (theme) => alpha(theme.palette.secondary.main, 0.05), + }, + cellStyle: (value) => ({ + position: "relative", + overflow: "hidden", + padding: "8px 16px", + backgroundColor: (theme) => alpha(theme.palette.secondary.main, 0.05), + }), + }, + } + ]; + + const optionalColumns = [ + { + accessorKey: "model.architecture", + header: createHeaderCell("Architecture", COLUMN_TOOLTIPS.ARCHITECTURE), + accessorFn: (row) => row.model.architecture, + cell: ({ row }) => ( + + {row.original.model.architecture || "-"} + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.architecture"], + }, + { + accessorKey: "model.precision", + header: createHeaderCell("Precision", COLUMN_TOOLTIPS.PRECISION), + accessorFn: (row) => row.model.precision, + cell: ({ row }) => ( + + {row.original.model.precision || "-"} + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.precision"], + }, + { + accessorKey: "metadata.params_billions", + header: createHeaderCell("Parameters", COLUMN_TOOLTIPS.PARAMETERS), + cell: ({ row }) => ( + + + {row.original.metadata.params_billions} + B + + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.params_billions"], + }, + { + accessorKey: "metadata.hub_license", + header: createHeaderCell("License", COLUMN_TOOLTIPS.LICENSE), + cell: ({ row }) => ( + + + {row.original.metadata.hub_license || "-"} + + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.hub_license"], + }, + { + accessorKey: "metadata.hub_hearts", + header: createHeaderCell( + "Hub ❤️", + "Number of likes received on the Hugging Face Hub" + ), + cell: ({ row }) => ( + + {row.original.metadata.hub_hearts} + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.hub_hearts"], + }, + { + accessorKey: "metadata.upload_date", + header: createHeaderCell( + "Upload Date", + "Date when the model was uploaded to the Hugging Face Hub" + ), + cell: ({ row }) => ( + + + {row.original.metadata.upload_date || "-"} + + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.upload_date"], + }, + { + accessorKey: "metadata.submission_date", + header: createHeaderCell( + "Submission Date", + "Date when the model was submitted to the leaderboard" + ), + cell: ({ row }) => ( + + + {row.original.metadata.submission_date || "-"} + + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.submission_date"], + }, + { + accessorKey: "metadata.generation", + header: createHeaderCell( + "Generation", + "The generation or version number of the model" + ), + cell: ({ row }) => ( + + {row.original.metadata.generation} + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.generation"], + }, + { + accessorKey: "metadata.base_model", + header: createHeaderCell( + "Base Model", + "The original model this model was derived from" + ), + cell: ({ row }) => ( + + + {row.original.metadata.base_model || "-"} + + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.base_model"], + }, + { + accessorKey: "metadata.co2_cost", + header: createHeaderCell("CO₂ Cost", COLUMN_TOOLTIPS.CO2_COST), + cell: ({ row }) => ( + + + {row.original.metadata.co2_cost?.toFixed(2) || "0"} + kg + + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["metadata.co2_cost"], + }, + { + accessorKey: "model.has_chat_template", + header: createHeaderCell( + "Chat Template", + "Whether this model has a chat template defined" + ), + cell: ({ row }) => ( + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["model.has_chat_template"], + }, + { + accessorKey: "features.is_not_available_on_hub", + header: createHeaderCell( + "Hub Availability", + "Whether the model is available on the Hugging Face Hub" + ), + cell: ({ row }) => ( + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES[ + "features.is_not_available_on_hub" + ], + }, + { + accessorKey: "features.is_highlighted_by_maintainer", + header: createHeaderCell( + "Official Providers", + "Models that are officially provided and maintained by their original creators or organizations" + ), + cell: ({ row }) => ( + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES[ + "features.is_highlighted_by_maintainer" + ], + enableSorting: true, + }, + { + accessorKey: "features.is_moe", + header: createHeaderCell( + "Mixture of Experts", + "Whether this model uses a Mixture of Experts architecture" + ), + cell: ({ row }) => , + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["features.is_moe"], + }, + { + accessorKey: "features.is_flagged", + header: createHeaderCell( + "Flag Status", + "Whether this model has been flagged for any issues" + ), + cell: ({ row }) => ( + + ), + size: TABLE_DEFAULTS.COLUMNS.COLUMN_SIZES["features.is_flagged"], + }, + ]; + + // Utiliser directement columnVisibility + const finalColumns = [ + ...baseColumns, + ...evaluationColumns, + ...optionalColumns + .filter((col) => columnVisibility[col.accessorKey]) + .sort((a, b) => { + // Définir l'ordre personnalisé des colonnes + const order = { + "model.architecture": 1, + "model.precision": 2, + "metadata.params_billions": 3, + "metadata.hub_license": 4, + "metadata.co2_cost": 5, + "metadata.hub_hearts": 6, + "metadata.upload_date": 7, + "metadata.submission_date": 8, + "metadata.generation": 9, + "metadata.base_model": 10, + "model.has_chat_template": 11, + "features.is_not_available_on_hub": 12, + "features.is_highlighted_by_maintainer": 13, + "features.is_moe": 14, + "features.is_flagged": 15, + }; + return order[a.accessorKey] - order[b.accessorKey]; + }), + ]; + + return finalColumns; +}; diff --git a/frontend/src/pages/LeaderboardPage/components/Leaderboard/utils/searchUtils.js b/frontend/src/pages/LeaderboardPage/components/Leaderboard/utils/searchUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..091796b7a7a3721b4d7f790f0fda75ca151a838d --- /dev/null +++ b/frontend/src/pages/LeaderboardPage/components/Leaderboard/utils/searchUtils.js @@ -0,0 +1,92 @@ +// Utility function to detect if a string looks like a regex +export const looksLikeRegex = (str) => { + const regexSpecialChars = /[\\^$.*+?()[\]{}|]/; + return regexSpecialChars.test(str); +}; + +// Function to map search fields to correct paths +const getFieldPath = (field) => { + const fieldMappings = { + precision: "model.precision", + architecture: "model.architecture", + license: "metadata.hub_license", + type: "model.type", + }; + return fieldMappings[field] || field; +}; + +// Function to extract special searches and normal text +export const parseSearchQuery = (query) => { + const specialSearches = []; + let remainingText = query; + + // Look for all @field:value patterns + const prefixRegex = /@\w+:/g; + const matches = query.match(prefixRegex) || []; + + matches.forEach((prefix) => { + const regex = new RegExp(`${prefix}([^\\s@]+)`, "g"); + remainingText = remainingText.replace(regex, (match, value) => { + const field = prefix.slice(1, -1); + specialSearches.push({ + field: getFieldPath(field), + displayField: field, + value, + }); + return ""; + }); + }); + + return { + specialSearches, + textSearch: remainingText.trim(), + }; +}; + +// Function to extract simple text search +export const extractTextSearch = (searchValue) => { + return searchValue + .split(";") + .map((query) => { + const { textSearch } = parseSearchQuery(query); + return textSearch; + }) + .filter(Boolean) + .join(";"); +}; + +// Utility function to access nested object properties +export const getValueByPath = (obj, path) => { + return path.split(".").reduce((acc, part) => acc?.[part], obj); +}; + +// Function to generate natural language description of the search +export const generateSearchDescription = (searchValue) => { + if (!searchValue) return null; + + const searchGroups = searchValue + .split(";") + .map((group) => group.trim()) + .filter(Boolean); + + return searchGroups.map((group, index) => { + const { specialSearches, textSearch } = parseSearchQuery(group); + + let parts = []; + if (textSearch) { + parts.push(textSearch); + } + + if (specialSearches.length > 0) { + const specialParts = specialSearches.map( + ({ displayField, value }) => `@${displayField}:${value}` + ); + parts = parts.concat(specialParts); + } + + return { + text: parts.join(" "), + index, + }; + }); +}; diff --git a/frontend/src/pages/QuotePage/QuotePage.js b/frontend/src/pages/QuotePage/QuotePage.js new file mode 100644 index 0000000000000000000000000000000000000000..c1e93f5a8db06271ebd7a813e76174b62bf005b1 --- /dev/null +++ b/frontend/src/pages/QuotePage/QuotePage.js @@ -0,0 +1,269 @@ +import React from "react"; +import { + Box, + Typography, + Paper, + IconButton, + Tooltip, + Alert, + Link, +} from "@mui/material"; +import ContentCopyIcon from "@mui/icons-material/ContentCopy"; +import PageHeader from "../../components/shared/PageHeader"; + +const citations = [ + { + title: "Plutus: Benchmarking Large Language Models in Low-Resource Greek Finance", + authors: + "Xueqing Peng et al.", + citation: `@misc{peng2025plutusbenchmarkinglargelanguage, + title={Plutus: Benchmarking Large Language Models in Low-Resource Greek Finance}, + author={Xueqing Peng and Triantafillos Papadopoulos and Efstathia Soufleri and Polydoros Giannouris and Ruoyu Xiang and Yan Wang and Lingfei Qian and Jimin Huang and Qianqian Xie and Sophia Ananiadou}, + year={2025}, + eprint={2502.18772}, + archivePrefix={arXiv}, + primaryClass={cs.CL}, + url={https://arxiv.org/abs/2502.18772}, +}`, + type: "main", + url: "https://arxiv.org/abs/2502.18772", + }, + { + title: "Evaluation Framework", + authors: "Leo Gao et al.", + citation: `@software{eval-harness, + author = {Gao, Leo and Tow, Jonathan and Biderman, Stella and Black, Sid and DiPofi, Anthony and Foster, Charles and Golding, Laurence and Hsu, Jeffrey and McDonell, Kyle and Muennighoff, Niklas and Phang, Jason and Reynolds, Laria and Tang, Eric and Thite, Anish and Wang, Ben and Wang, Kevin and Zou, Andy}, + title = {A framework for few-shot language model evaluation}, + month = sep, + year = 2021, + publisher = {Zenodo}, + version = {v0.0.1}, + doi = {10.5281/zenodo.5371628}, + url = {https://doi.org/10.5281/zenodo.5371628}, +}`, + url: "https://doi.org/10.5281/zenodo.5371628", + }, +]; + +const priorWork = [ + { + title: "The financial narrative summarisation shared task (FNS 2023)", + authors: + "Elias Zavitsanos et al.", + citation: `@inproceedings{zavitsanos2023financial, + title={The financial narrative summarisation shared task (FNS 2023)}, + author={Zavitsanos, Elias and Kosmopoulos, Aris and Giannakopoulos, George and Litvak, Marina and Carbajo-Coronado, Blanca and Moreno-Sandoval, Antonio and El-Haj, Mo}, + booktitle={2023 IEEE International Conference on Big Data (BigData)}, + pages={2890--2896}, + year={2023}, + organization={IEEE} +}`, + type: "main", + url: "https://ieeexplore.ieee.org/document/10386228", + }, + { + title: "MultiFin: A dataset for multilingual financial NLP", + authors: + "Rasmus J{\o}rgensen et al.", + citation: `@inproceedings{jorgensen2023multifin, + title={MultiFin: A dataset for multilingual financial NLP}, + author={J{\o}rgensen, Rasmus and Brandt, Oliver and Hartmann, Mareike and Dai, Xiang and Igel, Christian and Elliott, Desmond}, + booktitle={Findings of the Association for Computational Linguistics: EACL 2023}, + pages={894--909}, + year={2023} +}`, + type: "main", + url: "https://aclanthology.org/2023.findings-eacl.66/", + }, +// { +// title: "PIXIU: a large language model, instruction data and evaluation benchmark for finance", +// authors: +// "Qianqian Xie et al.", +// citation: `@inproceedings{10.5555/3666122.3667576, +// author = {Xie, Qianqian and Han, Weiguang and Zhang, Xiao and Lai, Yanzhao and Peng, Min and Lopez-Lira, Alejandro and Huang, Jimin}, +// title = {PIXIU: a large language model, instruction data and evaluation benchmark for finance}, +// year = {2024}, +// publisher = {Curran Associates Inc.}, +// address = {Red Hook, NY, USA}, +// abstract = {Although large language models (LLMs) have shown great performance in natural language processing (NLP) in the financial domain, there are no publicly available financially tailored LLMs, instruction tuning datasets, and evaluation benchmarks, which is critical for continually pushing forward the open-source development of financial artificial intelligence (AI). This paper introduces PIXIU, a comprehensive framework including the first financial LLM based on fine-tuning LLaMA with instruction data, the first instruction data with 128K data samples to support the fine-tuning, and an evaluation benchmark with 8 tasks and 15 datasets. We first construct the large-scale multi-task instruction data considering a variety of financial tasks, financial document types, and financial data modalities. We then propose a financial LLM called FinMA by fine-tuning LLaMA with the constructed dataset to be able to follow instructions for various financial tasks. To support the evaluation of financial LLMs, we propose a standardized benchmark that covers a set of critical financial tasks, including six financial NLP tasks and two financial prediction tasks. With this benchmark, we conduct a detailed analysis of FinMA and several existing LLMs, uncovering their strengths and weaknesses in handling critical financial tasks. The model, datasets, benchmark, and experimental results are open-sourced to facilitate future research in financial AI.}, +// booktitle = {Proceedings of the 37th International Conference on Neural Information Processing Systems}, +// articleno = {1454}, +// numpages = {16}, +// location = {New Orleans, LA, USA}, +// series = {NIPS '23} +// }`, +// type: "main", +// }, +]; + +const benchmarks = [ + { + title: "FinBen: A Holistic Financial Benchmark for Large Language Models", + authors: "Qianqian Xie et al.", + citation: `@article{xie2024finben, + title={The finben: An holistic financial benchmark for large language models}, + author={Xie, Qianqian and Han, Weiguang and Chen, Zhengyu and Xiang, Ruoyu and Zhang, Xiao and He, Yueru and Xiao, Mengxi and Li, Dong and Dai, Yongfu and Feng, Duanyu and others}, + journal={arXiv preprint arXiv:2402.12659}, + year={2024} +}`, + type: "main", + url: "https://arxiv.org/abs/2402.12659", + }, +// { +// title: "MultiFin: Instruction-Following Evaluation", +// authors: "Zhou et al.", +// citation: `@inproceedings{jorgensen-etal-2023-multifin, +// title = "{M}ulti{F}in: A Dataset for Multilingual Financial {NLP}", +// author = "J{\o}rgensen, Rasmus and +// Brandt, Oliver and +// Hartmann, Mareike and +// Dai, Xiang and +// Igel, Christian and +// Elliott, Desmond", +// editor = "Vlachos, Andreas and +// Augenstein, Isabelle", +// booktitle = "Findings of the Association for Computational Linguistics: EACL 2023", +// month = may, +// year = "2023", +// address = "Dubrovnik, Croatia", +// publisher = "Association for Computational Linguistics", +// url = "https://aclanthology.org/2023.findings-eacl.66/", +// doi = "10.18653/v1/2023.findings-eacl.66", +// pages = "894--909", +// abstract = "Financial information is generated and distributed across the world, resulting in a vast amount of domain-specific multilingual data. Multilingual models adapted to the financial domain would ease deployment when an organization needs to work with multiple languages on a regular basis. For the development and evaluation of such models, there is a need for multilingual financial language processing datasets. We describe MultiFin {--} a publicly available financial dataset consisting of real-world article headlines covering 15 languages across different writing systems and language families. The dataset consists of hierarchical label structure providing two classification tasks: multi-label and multi-class. We develop our annotation schema based on a real-world application and annotate our dataset using both {\textquoteleft}label by native-speaker' and {\textquoteleft}translate-then-label' approaches. The evaluation of several popular multilingual models, e.g., mBERT, XLM-R, and mT5, show that although decent accuracy can be achieved in high-resource languages, there is substantial room for improvement in low-resource languages." +// }`, +// url: "https://aclanthology.org/2023.findings-eacl.66/#:~:text=We%20describe%20MultiFin%20%2D%2D%20a,%2Dlabel%20and%20multi%2Dclass.", +// }, +]; + +const CitationBlock = ({ citation, title, authors, url, type }) => { + const handleCopy = () => { + navigator.clipboard.writeText(citation); + }; + + return ( + + + + {title} + + + {authors} + + {url && ( + + View paper → + + )} + + + + + + + + + {citation} + + + + ); +}; + +function QuotePage() { + return ( + + + + + + The citations below include both the leaderboard itself and the + individual benchmarks used in our evaluation suite. + + + + + + Leaderboard + + + {citations.map((citation, index) => ( + + ))} + + + + + + Benchmarks + + + {benchmarks.map((benchmark, index) => ( + + ))} + + + + + + Prior Work + + + {priorWork.map((citation, index) => ( + + ))} + + + + ); +} + +export default QuotePage; diff --git a/frontend/src/pages/VoteModelPage/VoteModelPage.js b/frontend/src/pages/VoteModelPage/VoteModelPage.js new file mode 100644 index 0000000000000000000000000000000000000000..c65684104e818d1b65df936c5e40e0044c75c3b2 --- /dev/null +++ b/frontend/src/pages/VoteModelPage/VoteModelPage.js @@ -0,0 +1,713 @@ +import React, { useState, useEffect } from "react"; +import { + Box, + Typography, + Paper, + Button, + Alert, + List, + ListItem, + CircularProgress, + Chip, + Divider, + IconButton, + Stack, + Link, +} from "@mui/material"; +import AccessTimeIcon from "@mui/icons-material/AccessTime"; +import PersonIcon from "@mui/icons-material/Person"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; +import HowToVoteIcon from "@mui/icons-material/HowToVote"; +import { useAuth } from "../../hooks/useAuth"; +import PageHeader from "../../components/shared/PageHeader"; +import AuthContainer from "../../components/shared/AuthContainer"; +import { alpha } from "@mui/material/styles"; +import CheckIcon from "@mui/icons-material/Check"; + +const NoModelsToVote = () => ( + + + + No Models to Vote + + + There are currently no models waiting for votes. +
    + Check back later! +
    +
    +); + +function VoteModelPage() { + const { isAuthenticated, user, loading } = useAuth(); + const [pendingModels, setPendingModels] = useState([]); + const [loadingModels, setLoadingModels] = useState(true); + const [error, setError] = useState(null); + const [userVotes, setUserVotes] = useState(new Set()); + + const formatWaitTime = (submissionTime) => { + if (!submissionTime) return "N/A"; + + const now = new Date(); + const submitted = new Date(submissionTime); + const diffInHours = Math.floor((now - submitted) / (1000 * 60 * 60)); + + // Less than 24 hours: show in hours + if (diffInHours < 24) { + return `${diffInHours}h`; + } + + // Less than 7 days: show in days + const diffInDays = Math.floor(diffInHours / 24); + if (diffInDays < 7) { + return `${diffInDays}d`; + } + + // More than 7 days: show in weeks + const diffInWeeks = Math.floor(diffInDays / 7); + return `${diffInWeeks}w`; + }; + + // Fetch user's votes + useEffect(() => { + const fetchUserVotes = async () => { + if (!isAuthenticated || !user) return; + + try { + // Récupérer les votes du localStorage + const localVotes = JSON.parse( + localStorage.getItem(`votes_${user.username}`) || "[]" + ); + const localVotesSet = new Set(localVotes); + + // Récupérer les votes du serveur + const response = await fetch(`/api/votes/user/${user.username}`); + if (!response.ok) { + throw new Error("Failed to fetch user votes"); + } + const data = await response.json(); + + // Fusionner les votes du serveur avec les votes locaux + const votedModels = new Set([ + ...data.map((vote) => vote.model), + ...localVotesSet, + ]); + setUserVotes(votedModels); + } catch (err) { + console.error("Error fetching user votes:", err); + } + }; + + fetchUserVotes(); + }, [isAuthenticated, user]); + + useEffect(() => { + const fetchModels = async () => { + try { + const response = await fetch("/api/models/pending"); + if (!response.ok) { + throw new Error("Failed to fetch pending models"); + } + const data = await response.json(); + + // Fetch votes for each model + const modelsWithVotes = await Promise.all( + data.map(async (model) => { + const [provider, modelName] = model.name.split("/"); + const votesResponse = await fetch( + `/api/votes/model/${provider}/${modelName}` + ); + const votesData = await votesResponse.json(); + + // Calculate total vote score from votes_by_revision + const totalScore = Object.values( + votesData.votes_by_revision || {} + ).reduce((a, b) => a + b, 0); + + // Calculate wait time based on submission_time from model data + const waitTimeDisplay = formatWaitTime(model.submission_time); + + return { + ...model, + votes: totalScore, + votes_by_revision: votesData.votes_by_revision, + wait_time: waitTimeDisplay, + hasVoted: userVotes.has(model.name), + }; + }) + ); + + // Sort models by vote score in descending order + const sortedModels = modelsWithVotes.sort((a, b) => b.votes - a.votes); + + setPendingModels(sortedModels); + } catch (err) { + setError(err.message); + } finally { + setLoadingModels(false); + } + }; + + fetchModels(); + }, [userVotes]); + + const handleVote = async (modelName) => { + if (!isAuthenticated) return; + + try { + // Disable the button immediately by adding the model to userVotes + setUserVotes((prev) => { + const newSet = new Set([...prev, modelName]); + // Sauvegarder dans le localStorage + if (user) { + const localVotes = JSON.parse( + localStorage.getItem(`votes_${user.username}`) || "[]" + ); + if (!localVotes.includes(modelName)) { + localVotes.push(modelName); + localStorage.setItem( + `votes_${user.username}`, + JSON.stringify(localVotes) + ); + } + } + return newSet; + }); + + // Split modelName into provider and model + const [provider, model] = modelName.split("/"); + + const response = await fetch( + `/api/votes/${modelName}?vote_type=up&user_id=${user.username}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + // Si le vote échoue, on retire le vote du localStorage et du state + setUserVotes((prev) => { + const newSet = new Set(prev); + newSet.delete(modelName); + if (user) { + const localVotes = JSON.parse( + localStorage.getItem(`votes_${user.username}`) || "[]" + ); + const updatedVotes = localVotes.filter( + (vote) => vote !== modelName + ); + localStorage.setItem( + `votes_${user.username}`, + JSON.stringify(updatedVotes) + ); + } + return newSet; + }); + throw new Error("Failed to submit vote"); + } + + // Refresh votes for this model + const votesResponse = await fetch( + `/api/votes/model/${provider}/${model}` + ); + const votesData = await votesResponse.json(); + + // Calculate total vote score from votes_by_revision + const totalScore = Object.values( + votesData.votes_by_revision || {} + ).reduce((a, b) => a + b, 0); + + // Update model and resort the list + setPendingModels((models) => { + const updatedModels = models.map((model) => + model.name === modelName + ? { + ...model, + votes: totalScore, + votes_by_revision: votesData.votes_by_revision, + } + : model + ); + return updatedModels.sort((a, b) => b.votes - a.votes); + }); + } catch (err) { + setError(err.message); + } + }; + + if (loading) { + return ( + + + + ); + } + + return ( + + + Help us prioritize which + models to evaluate next + + } + /> + + {error && ( + + {error} + + )} + + {/* Auth Status */} + {/* + {isAuthenticated ? ( + + + + + Connected as {user?.username} + + + + + + + ) : ( + + + Login to Vote + + + You need to be logged in with your Hugging Face account to vote + for models + + + + )} + */} + + + {/* Models List */} + + {/* Header - Always visible */} + + theme.palette.mode === "dark" + ? alpha(theme.palette.divider, 0.1) + : "grey.200", + bgcolor: (theme) => + theme.palette.mode === "dark" + ? alpha(theme.palette.background.paper, 0.5) + : "grey.50", + }} + > + + Models Pending Evaluation + + + + {/* Table Header */} + + + + Model + + + + + Votes + + + + + Priority + + + + + {/* Content */} + {loadingModels ? ( + + + + ) : pendingModels.length === 0 && !loadingModels ? ( + + ) : ( + + {pendingModels.map((model, index) => { + const isTopThree = index < 3; + return ( + + {index > 0 && } + + {/* Left side - Model info */} + + + {/* Model name and link */} + + + {model.name} + + + + + + {/* Metadata row */} + + + + + {model.wait_time} + + + + + + {model.submitter} + + + + + + + {/* Vote Column */} + + + + + + + + + + {model.votes > 999 ? "999" : model.votes} + + + + votes + + + + + + + {/* Priority Column */} + + + {isTopThree && ( + + HIGH + + )} + + #{index + 1} + + + } + size="medium" + variant={isTopThree ? "filled" : "outlined"} + sx={{ + height: 36, + minWidth: "100px", + bgcolor: isTopThree + ? (theme) => alpha(theme.palette.primary.main, 0.1) + : "transparent", + borderColor: isTopThree ? "primary.main" : "grey.300", + borderWidth: 2, + "& .MuiChip-label": { + px: 2, + fontSize: "0.95rem", + }, + }} + /> + + + + ); + })} + + )} + + + ); +} + +export default VoteModelPage;