diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..86b3638d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.venv +node_modules +dist +.git +.github +*.md +!README.md +__pycache__ +*.pyc +.env +.env.* +e2e +tests diff --git a/.env.example b/.env.example index 94b8210c..c37f9772 100644 --- a/.env.example +++ b/.env.example @@ -42,29 +42,61 @@ FALKORDB_URL=redis://localhost:6379/0 # REQUIRED - change to your FalkorDB URL # FALKORDB_PORT=6379 # ----------------------------- -# Optional API / secret tokens +# API / secret tokens # ----------------------------- -# API token for internal API access (optional) -# SECRET_TOKEN=your_secret_token +# Optional: static bearer token for internal / programmatic API access. +# When set, this token is accepted as a "master" API key. +# When unset, only user-generated tokens are accepted. # SECRET_TOKEN_ERP=your_erp_token # ----------------------------- # AI / LLM configuration (optional) # ----------------------------- -# The default is to use Azure OpenAI if all three variables are set. -# If the OPENAI_API_KEY is set, it will use OpenAI directly. +# QueryWeaver supports multiple AI providers. Set ONE of the following API keys. +# Provider selection precedence (first match wins): +# OLLAMA_MODEL > OPENAI_API_KEY > GEMINI_API_KEY > ANTHROPIC_API_KEY > COHERE_API_KEY > Azure (fallback) +# +# Optional: Override the default models (both must be from the SAME provider): +# COMPLETION_MODEL=provider/model-name +# EMBEDDING_MODEL=provider/model-name +# +# IMPORTANT: The provider must match your API key. If you set gemini/* models, +# you must have GEMINI_API_KEY set. Mismatched overrides may cause runtime errors. +# +# Examples (using Gemini - requires GEMINI_API_KEY): +# COMPLETION_MODEL=gemini/gemini-3-pro-preview +# EMBEDDING_MODEL=gemini/gemini-embedding-001 -# Azure OpenAI (example) -# AZURE_API_KEY=your_azure_api_key -# AZURE_API_BASE=https://your-resource.openai.azure.com/ -# AZURE_API_VERSION=2023-05-15 +# Examples (using OpenAI - requires OPENAI_API_KEY): +# COMPLETION_MODEL=openai/gpt-4.1 +# EMBEDDING_MODEL=openai/text-embedding-ada-002 -# OpenAI (example) +# OpenAI - uses openai/gpt-4.1 and openai/text-embedding-ada-002 # OPENAI_API_KEY=your_openai_api_key -# Optional: override default model names from api/config.py -# COMPLETION_MODEL=azure/gpt-4.1 -# EMBEDDING_MODEL=azure/text-embedding-ada-002 +# Google Gemini - uses gemini/gemini-3-pro-preview and gemini/gemini-embedding-001 +# GEMINI_API_KEY=your_gemini_api_key + +# Anthropic - uses anthropic/claude-sonnet-4-5-20250929 +# Note: Anthropic has no native embeddings. You MUST also set one of: +# VOYAGE_API_KEY or EMBEDDING_MODEL for embeddings (startup fails otherwise). +# ANTHROPIC_API_KEY=your_anthropic_api_key +# VOYAGE_API_KEY=your_voyage_api_key # Optional: for Voyage AI embeddings with Anthropic + +# Cohere - uses cohere/command-a-03-2025 and cohere/embed-v4.0 +# COHERE_API_KEY=your_cohere_api_key +# COHERE_MODEL=command-a-03-2025 +# COHERE_EMBEDDING_MODEL=embed-v4.0 + +# Local open-source model via Ollama (through LiteLLM) +# OLLAMA_MODEL=llama3.1 +# OLLAMA_EMBEDDING_MODEL=nomic-embed-text +# OLLAMA_API_BASE=http://localhost:11434 + +# Azure OpenAI (default fallback) - uses azure/gpt-4.1 and azure/text-embedding-ada-002 +# AZURE_API_KEY=your_azure_api_key +# AZURE_API_BASE=https://your-resource.openai.azure.com/ +# AZURE_API_VERSION=2023-05-15 # ----------------------------- # OAuth configuration (optional — uncomment to enable login flows) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 32367ffb..7f74723f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -10,7 +10,7 @@ QueryWeaver is an open-source Text2SQL tool that transforms natural language int - **Backend**: Python 3.12+, FastAPI 0.115.0+, FalkorDB (Redis-based graph database) - **AI/ML**: LiteLLM with Azure OpenAI/OpenAI integration for text-to-SQL generation - **Testing**: pytest for unit tests, Playwright for E2E testing -- **Dependencies**: pipenv for package management +- **Dependencies**: uv for package management - **Authentication**: authlib with Google/GitHub OAuth - **Deployment**: Docker support, Vercel configuration @@ -22,8 +22,9 @@ Follow this order for a reliable local setup; if you customize the steps, ensure ### 1. Initial Setup (recommended for new contributors) ```bash -# Install pipenv if not available -pip install pipenv +# Install uv if not available +pip install uv +# or visit https://docs.astral.sh/uv/getting-started/installation/ # Install dependencies (backend + frontend) and prepare dev tools # Recommended: use the Make helper which installs Python deps and frontend deps @@ -34,9 +35,9 @@ make install make setup-dev # OR manual steps if you prefer more granular control: -# pipenv sync --dev -# pipenv run playwright install chromium -# pipenv run playwright install-deps +# uv sync +# uv run playwright install chromium +# uv run playwright install-deps # Set up environment file cp .env.example .env @@ -51,9 +52,9 @@ Note: This project includes a TypeScript frontend in `app/` that must be built b make setup-dev # OR manual steps: -pipenv sync --dev -pipenv run playwright install chromium -pipenv run playwright install-deps +uv sync +uv run playwright install chromium +uv run playwright install-deps ``` ### 3. Testing Commands @@ -82,7 +83,7 @@ make docker-stop ```bash # Run pylint (can be run without FalkorDB) make lint -# OR manually: pipenv run pylint $(git ls-files '*.py') +# OR manually: uv run pylint $(git ls-files '*.py') ``` ### 5. Running the Application @@ -90,11 +91,11 @@ make lint ```bash # Development server with debug mode make run-dev -# OR manually: pipenv run uvicorn api.index:app --host "localhost" --port "5000" --reload +# OR manually: uv run uvicorn api.index:app --host "localhost" --port "5000" --reload # Production mode make run-prod -# OR manually: pipenv run uvicorn api.index:app --host "localhost" --port "5000" +# OR manually: uv run uvicorn api.index:app --host "localhost" --port "5000" ``` Important: If you're preparing a production deployment or have changed frontend code, run `make build-prod` (or `make build-dev` for a development build) first to produce the static bundle used by the app. @@ -183,8 +184,8 @@ make docker-falkordb **Error**: E2E tests fail with browser not found **Solution**: ```bash -pipenv run playwright install chromium -pipenv run playwright install-deps +uv run playwright install chromium +uv run playwright install-deps ``` ### 3. Missing Environment File @@ -197,10 +198,10 @@ cp .env.example .env ### 4. Import Errors During Testing **Error**: Module import failures in tests -**Solution**: Ensure you're using pipenv and dependencies are installed: +**Solution**: Ensure you're using uv and dependencies are installed: ```bash -pipenv sync --dev -pipenv run pytest tests/ -k "not e2e" +uv sync +uv run python -m pytest tests/ -k "not e2e" ``` ### 5. Port Conflicts @@ -242,7 +243,7 @@ tests/ ``` ### Configuration Files -- `Pipfile` & `Pipfile.lock`: Python dependencies +- `pyproject.toml` & `uv.lock`: Python dependencies - `pytest.ini`: Test configuration with custom markers - `Makefile`: Build and development commands - `.env.example`: Environment variable template @@ -268,7 +269,7 @@ The repository has comprehensive CI/CD in `.github/workflows/`: 2. **pylint.yml**: Code quality checks - Runs on every push - - Uses same Python/pipenv setup + - Uses same Python/uv setup 3. **e2e-tests.yml**: Dedicated E2E testing - Separate workflow for E2E tests @@ -281,8 +282,8 @@ The repository has comprehensive CI/CD in `.github/workflows/`: All workflows follow this pattern: ```yaml - Python 3.12 setup -- pipenv installation -- pipenv sync --dev +- uv installation +- uv sync - .env file creation with test values (use FALKORDB_URL in CI) - FalkorDB service startup (for tests requiring DB) - Playwright browser installation (for E2E tests) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bcb81510..8fa39381 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,7 +10,7 @@ updates: target-branch: "staging" schedule: interval: "weekly" - - package-ecosystem: "pip" + - package-ecosystem: "uv" directory: "/" target-branch: "staging" schedule: diff --git a/.github/wordlist.txt b/.github/wordlist.txt index 0bbe3fe9..5eacb0df 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -1,6 +1,8 @@ QueryWeaver FalkorDB OAuth +DDL +DML AGPL Affero nullability @@ -8,6 +10,7 @@ schemas psycopg html PostgreSQLLoader +PostgresLoader api postgres postgresql @@ -30,8 +33,8 @@ Nullability endcapture CVSS falkordb -pipenv -Pipenv +uv +pyproject README md UI @@ -71,9 +74,13 @@ namespace namespaced CSRF +Anthropic +Cohere LLM +Ollama OpenAI OpenAI's +DockerHub Dockerhub FDE github @@ -95,4 +102,22 @@ Sanitization JOINs subqueries subquery -TTL \ No newline at end of file +TTL + +config +docstring +dotenv +ESLint +GraphNotFoundError +HSTS +init +InternalError +InvalidArgumentError +Middleware +monorepo +PRs +pylint +pytest +Radix +Zod +Dependabot diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 4e43cdf8..e0c3be3e 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -29,12 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout repository' - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: 'Dependency Review' - uses: actions/dependency-review-action@v4 + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4 # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. with: comment-summary-in-pr: always - # fail-on-severity: moderate - # deny-licenses: GPL-1.0-or-later, LGPL-2.0-or-later - # retry-on-snapshot-warnings: true diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 94891b53..c0d9d871 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -19,36 +19,41 @@ env: jobs: test: + # Skip for Dependabot PRs — the LLM-dependent E2E tests require Azure OpenAI + # secrets that are not available in Dependabot PR runs. Full E2E coverage runs + # on the merge-to-staging/main push event where secrets are accessible. + if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]' timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 # Setup Python - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: ${{ env.PYTHON_VERSION }} - cache: 'pip' - cache-dependency-path: Pipfile.lock - + + # Install uv + - name: Install uv + uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + with: + version: "0.7.12" + # Setup Node.js - - uses: actions/setup-node@v6 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' cache-dependency-path: | package-lock.json app/package-lock.json - - # Install pipenv - - name: Install pipenv - run: pip install pipenv - + + # Install Python dependencies - name: Install Python dependencies - run: pipenv sync --dev + run: uv sync --locked # Install Node dependencies (root - for Playwright) - name: Install root dependencies @@ -57,7 +62,7 @@ jobs: # Cache Playwright browsers - name: Cache Playwright browsers id: playwright-cache - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: ~/.cache/ms-playwright key: playwright-browsers-${{ runner.os }}-${{ hashFiles('package-lock.json') }} @@ -107,12 +112,25 @@ jobs: echo "Testing FalkorDB connectivity from host..." docker ps -a + # Create CI .env so load_dotenv() finds the required variables + - name: Create CI .env + run: | + cat > .env < /tmp/uvicorn.log 2>&1 & + # Start server in background with uv + uv run uvicorn api.index:app --host localhost --port 5000 > /tmp/uvicorn.log 2>&1 & UVICORN_PID=$! + echo "pid=$UVICORN_PID" >> "$GITHUB_OUTPUT" echo "Started server with PID: $UVICORN_PID" # Wait for app to start echo "Waiting for application to start..." @@ -162,7 +180,7 @@ jobs: CI: true # Upload test results on failure - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 if: failure() with: name: playwright-report @@ -170,17 +188,18 @@ jobs: retention-days: 30 # Upload test screenshots on failure - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 if: failure() with: name: test-results path: test-results/ retention-days: 30 - # Cleanup - Stop all containers - - name: Cleanup Docker containers + # Cleanup - Stop all containers and background processes + - name: Cleanup if: always() run: | + kill ${{ steps.start-server.outputs.pid }} || true docker compose -f e2e/docker-compose.test.yml down -v || true docker stop falkordb-test || true docker rm falkordb-test || true diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 8eb2753b..265bd456 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -12,22 +12,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Log in to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: images: falkordb/queryweaver - name: Build and push Docker image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7 with: context: . push: true diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 08930af1..57da534b 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -9,22 +9,22 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.12' - - name: Install pipenv - run: | - python -m pip install --upgrade pip - pip install pipenv + - name: Install uv + uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + with: + version: "latest" - name: Install dependencies run: | - pipenv sync --dev + uv sync - name: Run pylint run: | - pipenv run pylint $(git ls-files '*.py') \ No newline at end of file + uv run python -m pylint $(git ls-files '*.py') \ No newline at end of file diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 7fce97f2..ffcc1d88 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -10,9 +10,9 @@ jobs: spellcheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Spellcheck - uses: rojopolis/spellcheck-github-actions@0.58.0 + uses: rojopolis/spellcheck-github-actions@e3cd8e9aec4587ec73bc0e60745aafd45c37aa2e # 0.60.0 with: config_path: .github/spellcheck-settings.yml task_name: Markdown \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bba98e22..f393b892 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,21 +29,21 @@ jobs: --health-retries 5 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.12' - - name: Install pipenv - run: | - python -m pip install --upgrade pip - pip install pipenv + - name: Install uv + uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + with: + version: "0.7.12" - name: Install dependencies run: | - pipenv sync --dev + uv sync --locked - name: Install frontend dependencies run: | @@ -63,7 +63,7 @@ jobs: - name: Run unit tests run: | - pipenv run pytest tests/ -k "not e2e" --verbose + uv run python -m pytest tests/ -k "not e2e" --verbose - name: Run lint run: | diff --git a/.gitignore b/.gitignore index a9e199cd..3bf6a813 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,7 @@ demo_tokens.py /blob-report/ /playwright/.cache/ /playwright/.auth/ -e2e/.auth/ \ No newline at end of file +e2e/.auth/ +# Build artifacts +clients/python/queryweaver_client.egg-info/ +clients/ts/dist/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..e99aae12 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,147 @@ +# AGENTS.md — QueryWeaver (Text2SQL) + +## Project Overview + +QueryWeaver is an open-source Text2SQL tool that converts natural-language questions into SQL using graph-powered schema understanding backed by FalkorDB. It is a full-stack monorepo with a Python/FastAPI backend (`api/`) and a React/TypeScript frontend (`app/`). + +Repository: `FalkorDB/QueryWeaver` + +## Tech Stack + +| Layer | Technology | +|-------|-----------| +| Backend | Python 3.12+, FastAPI, Uvicorn | +| Frontend | React 18, TypeScript 5.8, Vite 7, Tailwind CSS | +| Graph DB | FalkorDB | +| LLM | LiteLLM (multi-provider: OpenAI, Gemini, Anthropic, Cohere, Azure, Ollama) | +| Auth | OAuth 2.0 (Google, GitHub) via authlib | +| Package mgmt | uv (Python), npm (Node) | +| Testing | pytest (unit), Playwright (E2E) | +| Linting | pylint (Python), ESLint (TypeScript) | +| CI/CD | GitHub Actions | + +## Directory Structure + +``` +api/ Python backend (FastAPI) + agents/ AI agents (analysis, healing, relevancy, follow-up) + analyzers/ Code/syntax analyzers + auth/ OAuth handlers, user management + core/ Core text2sql logic, schema loading, errors + entities/ Data models / DTOs + loaders/ Database loaders (PostgreSQL, MySQL) + memory/ Conversation memory management + routes/ API endpoints (auth, graphs, database, tokens, settings) + sql_utils/ SQL sanitization + config.py LLM provider detection, configuration + app_factory.py FastAPI app init, middleware + index.py Application entry point +app/ React + Vite frontend + src/components/ React components + src/contexts/ React contexts (Auth, Chat, Database, Settings) + src/services/ API service layer + src/types/ TypeScript type definitions +tests/ Unit tests (pytest) +e2e/ End-to-end tests (Playwright) +docs/ Documentation +.github/workflows/ CI/CD pipelines +``` + +## Quick Reference + +### Install & Setup + +```bash +make install # uv sync + npm ci +make setup-dev # install + Playwright browsers +cp .env.example .env # configure environment +``` + +### Run + +```bash +make run-dev # dev server with hot reload (localhost:5000) +make run-prod # production server +make docker-falkordb # start FalkorDB in Docker +``` + +### Test + +```bash +make test-unit # pytest unit tests +make test-e2e # Playwright E2E (headless) +make test-e2e-headed # Playwright E2E (visible browser) +make test # build + unit + E2E +``` + +### Lint + +```bash +make lint # pylint + ESLint +make lint-frontend # ESLint only +``` + +### Build + +```bash +make build-dev # Vite dev build +make build-prod # Vite production build +``` + +## Code Conventions + +### Python (backend) + +- PEP 8 with **120-char line limit** +- Type hints throughout +- pylint for linting (docstring checks disabled) +- FastAPI routers split by domain under `api/routes/` +- Custom exceptions in `api/core/errors.py` (GraphNotFoundError, InternalError, InvalidArgumentError) +- Middleware: CSRF (double-submit cookies), HSTS headers +- Environment config via dotenv; see `api/config.py` for defaults and provider detection +- Run backend: `uv run uvicorn api.index:app` + +### TypeScript / React (frontend) + +- Strict TypeScript +- ESLint with `@typescript-eslint`; unused vars prefixed with `_` are allowed +- State management via React Context API +- API calls through service layer (`app/src/services/`) +- Styling with Tailwind CSS + Radix UI primitives +- Routing with React Router v7 +- Forms with React Hook Form + Zod validation +- Build tool: Vite (dev proxy to backend on port 5000) + +### Testing + +- **Unit tests** (`tests/`): pytest with markers `e2e`, `slow`, `auth`, `integration`, `unit` +- **E2E tests** (`e2e/`): Playwright with Page Object Model pattern; auth setup runs first +- E2E infra lives in `e2e/infra/`, page objects in `e2e/logic/pom/` +- Test data (SQL init scripts) in `e2e/test-data/` + +## Environment Variables + +Required: +- `FASTAPI_SECRET_KEY` — session secret +- `FALKORDB_URL` — FalkorDB connection string (e.g. `redis://localhost:6379/0`) + +LLM provider (set one): `OPENAI_API_KEY`, `GEMINI_API_KEY`, `ANTHROPIC_API_KEY`, `COHERE_API_KEY`, `AZURE_API_KEY`, or `OLLAMA_MODEL` + +Optional overrides: `COMPLETION_MODEL`, `EMBEDDING_MODEL` (must match provider) + +See `.env.example` for the full list. + +## CI/CD + +GitHub Actions workflows (`.github/workflows/`): +- **tests.yml** — unit tests + lint on push/PR to main/staging +- **playwright.yml** — dedicated Playwright E2E suite (skipped for Dependabot PRs; secrets unavailable) +- **pylint.yml** — Python linting +- **spellcheck.yml** — docs spellcheck +- **publish-docker.yml** — build & push Docker image to DockerHub +- **dependency-review.yml** — dependency security review + +## Branching + +- `main` — production branch, target for PRs +- `staging` — integration branch diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a9517927..972e9c86 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,14 +28,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /app -# Install pipenv -RUN python3 -m pip install --no-cache-dir --break-system-packages pipenv +# Install uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv -# Copy Pipfile and Pipfile.lock -COPY Pipfile Pipfile.lock ./ +# Copy pyproject.toml, uv.lock, and README.md (needed by hatchling during install) +COPY pyproject.toml uv.lock* README.md ./ -# Install Python dependencies from Pipfile -RUN PIP_BREAK_SYSTEM_PACKAGES=1 pipenv sync --system +# Install packages into system Python (no virtualenv in container) +ENV UV_SYSTEM_PYTHON=1 + +# Ensure venv binaries are on PATH (uv sync always creates .venv) +ENV PATH="/app/.venv/bin:$PATH" + +# Install Python dependencies only (project itself installed after COPY) +RUN uv sync --frozen --no-dev --no-install-project # Install Node.js (Node 22) so we can build the frontend inside the image. # Use NodeSource setup script to get a recent Node version on Debian-based images. @@ -69,6 +75,9 @@ RUN npm --prefix ./app run build # Copy application code COPY . . +# Install the project package now that source code is available +RUN uv sync --frozen --no-dev + # Copy and make start.sh executable COPY start.sh /start.sh RUN chmod +x /start.sh diff --git a/Makefile b/Makefile index 4ebeda8b..54b5ac9a 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,13 @@ help: ## Show this help message @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST) install: ## Install dependencies - pipenv sync --dev - npm install --prefix ./app + uv sync + npm ci --prefix ./app setup-dev: install ## Set up development environment - pipenv run playwright install chromium - pipenv run playwright install-deps + uv run playwright install chromium + uv run playwright install-deps @echo "Development environment setup complete!" @echo "Don't forget to copy .env.example to .env and configure your settings" @@ -26,23 +26,23 @@ build-prod: test: build-dev test-unit test-e2e ## Run all tests test-unit: ## Run unit tests only - pipenv run python -m pytest tests/ -k "not e2e" --verbose + uv run python -m pytest tests/ -k "not e2e" --verbose test-e2e: build-dev ## Run E2E tests headless - pipenv run python -m pytest tests/e2e/ --browser chromium --video=on --screenshot=on + uv run python -m pytest tests/e2e/ --browser chromium --video=on --screenshot=on test-e2e-headed: build-dev ## Run E2E tests with browser visible - pipenv run python -m pytest tests/e2e/ --browser chromium --headed + uv run python -m pytest tests/e2e/ --browser chromium --headed test-e2e-debug: build-dev ## Run E2E tests with debugging enabled - pipenv run python -m pytest tests/e2e/ --browser chromium --slowmo=1000 + uv run python -m pytest tests/e2e/ --browser chromium --slowmo=1000 lint: ## Run linting (backend + frontend) @echo "Running backend lint (pylint)" - pipenv run pylint $(shell git ls-files '*.py') || true + uv run python -m pylint $(shell git ls-files '*.py') @echo "Running frontend lint (eslint)" make lint-frontend @@ -61,10 +61,10 @@ clean: ## Clean up test artifacts find . -name "*.pyo" -delete run-dev: build-dev ## Run development server - pipenv run uvicorn api.index:app --host $${HOST:-127.0.0.1} --port $${PORT:-5000} --reload + uv run uvicorn api.index:app --host $${HOST:-127.0.0.1} --port $${PORT:-5000} --reload run-prod: build-prod ## Run production server - pipenv run uvicorn api.index:app --host $${HOST:-0.0.0.0} --port $${PORT:-5000} + uv run uvicorn api.index:app --host $${HOST:-0.0.0.0} --port $${PORT:-5000} docker-falkordb: ## Start FalkorDB in Docker for testing docker run -d --name falkordb-test -p 6379:6379 falkordb/falkordb:latest diff --git a/Pipfile b/Pipfile deleted file mode 100644 index c1c7fa77..00000000 --- a/Pipfile +++ /dev/null @@ -1,30 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -fastapi = "~=0.135.0" -uvicorn = "~=0.41.0" -litellm = "~=1.82.0" -falkordb = "~=1.6.0" -psycopg2-binary = "~=2.9.11" -pymysql = "~=1.1.0" -authlib = "~=1.6.4" -itsdangerous = "~=2.2.0" -jsonschema = "~=4.26.0" -tqdm = "~=4.67.3" -python-multipart = "~=0.0.10" -jinja2 = "~=3.1.4" -graphiti-core = {ref = "staging", git = "git+https://github.com/FalkorDB/graphiti.git"} -fastmcp = ">=2.13.1" - -[dev-packages] -pytest = "~=8.4.2" -pylint = "~=4.0.3" -playwright = "~=1.58.0" -pytest-playwright = "~=0.7.1" -pytest-asyncio = "~=1.2.0" - -[requires] -python_version = "3.12" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index a01f66f2..00000000 --- a/Pipfile.lock +++ /dev/null @@ -1,3289 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "1fba6f108fd74d27ba11d829c89ee35b72233b52c5efefd802e877194ff152e8" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.12" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "aiofile": { - "hashes": [ - "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa", - "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b" - ], - "markers": "python_version >= '3.8' and python_version < '4'", - "version": "==3.9.0" - }, - "aiohappyeyeballs": { - "hashes": [ - "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", - "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8" - ], - "markers": "python_version >= '3.9'", - "version": "==2.6.1" - }, - "aiohttp": { - "hashes": [ - "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", - "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", - "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", - "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", - "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", - "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", - "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", - "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", - "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", - "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", - "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", - "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", - "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", - "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", - "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", - "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80", - "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", - "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", - "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", - "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", - "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", - "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e", - "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", - "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", - "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", - "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", - "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", - "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", - "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", - "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", - "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f", - "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", - "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", - "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", - "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", - "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce", - "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", - "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1", - "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", - "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", - "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", - "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", - "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", - "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", - "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", - "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", - "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", - "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", - "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", - "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", - "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3", - "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", - "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", - "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", - "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", - "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", - "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", - "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", - "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", - "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", - "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", - "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", - "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", - "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", - "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", - "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", - "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", - "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6", - "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", - "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", - "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43", - "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", - "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7", - "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7", - "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", - "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", - "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02", - "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", - "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", - "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6", - "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", - "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", - "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", - "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", - "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", - "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", - "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", - "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", - "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", - "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", - "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877", - "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", - "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", - "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", - "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704", - "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a", - "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", - "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", - "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", - "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", - "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", - "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", - "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", - "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", - "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538", - "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", - "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", - "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", - "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", - "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", - "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", - "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558", - "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", - "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", - "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", - "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", - "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", - "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", - "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", - "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940" - ], - "markers": "python_version >= '3.9'", - "version": "==3.13.3" - }, - "aiosignal": { - "hashes": [ - "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", - "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7" - ], - "markers": "python_version >= '3.9'", - "version": "==1.4.0" - }, - "annotated-doc": { - "hashes": [ - "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", - "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4" - ], - "markers": "python_version >= '3.8'", - "version": "==0.0.4" - }, - "annotated-types": { - "hashes": [ - "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", - "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" - ], - "markers": "python_version >= '3.8'", - "version": "==0.7.0" - }, - "anyio": { - "hashes": [ - "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", - "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c" - ], - "markers": "python_version >= '3.9'", - "version": "==4.12.1" - }, - "attrs": { - "hashes": [ - "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", - "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" - ], - "markers": "python_version >= '3.9'", - "version": "==25.4.0" - }, - "authlib": { - "hashes": [ - "sha256:41ae180a17cf672bc784e4a518e5c82687f1fe1e98b0cafaeda80c8e4ab2d1cb", - "sha256:97286fd7a15e6cfefc32771c8ef9c54f0ed58028f1322de6a2a7c969c3817888" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==1.6.8" - }, - "backoff": { - "hashes": [ - "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", - "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8" - ], - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==2.2.1" - }, - "beartype": { - "hashes": [ - "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", - "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2" - ], - "markers": "python_version >= '3.10'", - "version": "==0.22.9" - }, - "cachetools": { - "hashes": [ - "sha256:8f086515c254d5664ae2146d14fc7f65c9a4bce75152eb247e5a9c5e6d7b2ecf", - "sha256:e31e579d2c5b6e2944177a0397150d312888ddf4e16e12f1016068f0c03b8341" - ], - "markers": "python_version >= '3.10'", - "version": "==7.0.1" - }, - "caio": { - "hashes": [ - "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", - "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", - "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", - "sha256:44a6b58e52d488c75cfaa5ecaa404b2b41cc965e6c417e03251e868ecd5b6d77", - "sha256:97084e4e30dfa598449d874c4d8e0c8d5ea17d2f752ef5e48e150ff9d240cd64", - "sha256:bf84bfa039f25ad91f4f52944452a5f6f405e8afab4d445450978cd6241d1478", - "sha256:ca6c8ecda611478b6016cb94d23fd3eb7124852b985bdec7ecaad9f3116b9619", - "sha256:d6956d9e4a27021c8bd6c9677f3a59eb1d820cc32d0343cea7961a03b1371965", - "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", - "sha256:db9b5681e4af8176159f0d6598e73b2279bb661e718c7ac23342c550bd78c241", - "sha256:fab6078b9348e883c80a5e14b382e6ad6aabbc4429ca034e76e730cf464269db", - "sha256:fb7ff95af4c31ad3f03179149aab61097a71fd85e05f89b4786de0359dffd044" - ], - "markers": "python_version >= '3.10'", - "version": "==0.9.25" - }, - "certifi": { - "hashes": [ - "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", - "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7" - ], - "markers": "python_version >= '3.7'", - "version": "==2026.2.25" - }, - "cffi": { - "hashes": [ - "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", - "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", - "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", - "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", - "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", - "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", - "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", - "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", - "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", - "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", - "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", - "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", - "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", - "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", - "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", - "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", - "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", - "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", - "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", - "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", - "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", - "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", - "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", - "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", - "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", - "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", - "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", - "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", - "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", - "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", - "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", - "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", - "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", - "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", - "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", - "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", - "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", - "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", - "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", - "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", - "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", - "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", - "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", - "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", - "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", - "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", - "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", - "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", - "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", - "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", - "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", - "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", - "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", - "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", - "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", - "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", - "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", - "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", - "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", - "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", - "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", - "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", - "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", - "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", - "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", - "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", - "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", - "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", - "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", - "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", - "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", - "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", - "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", - "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", - "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", - "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", - "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", - "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", - "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", - "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", - "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", - "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", - "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", - "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf" - ], - "markers": "python_version >= '3.9'", - "version": "==2.0.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", - "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", - "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", - "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", - "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", - "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", - "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63", - "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", - "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", - "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", - "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", - "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", - "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", - "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af", - "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", - "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", - "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", - "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", - "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", - "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", - "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576", - "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", - "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", - "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", - "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", - "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", - "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", - "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", - "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", - "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", - "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", - "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", - "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a", - "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", - "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", - "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", - "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", - "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", - "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7", - "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", - "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", - "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", - "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", - "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", - "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", - "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2", - "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", - "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", - "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", - "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", - "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", - "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", - "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", - "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", - "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa", - "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", - "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", - "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", - "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", - "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", - "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", - "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", - "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", - "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", - "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", - "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", - "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", - "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", - "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", - "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", - "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3", - "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", - "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", - "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", - "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", - "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", - "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", - "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf", - "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", - "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", - "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", - "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", - "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", - "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", - "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", - "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", - "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", - "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4", - "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", - "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", - "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", - "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", - "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", - "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", - "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", - "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", - "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", - "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", - "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074", - "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", - "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", - "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", - "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", - "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d", - "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", - "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", - "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", - "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", - "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", - "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", - "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", - "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", - "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" - ], - "markers": "python_version >= '3.7'", - "version": "==3.4.4" - }, - "click": { - "hashes": [ - "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", - "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" - ], - "markers": "python_version >= '3.10'", - "version": "==8.3.1" - }, - "cryptography": { - "hashes": [ - "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", - "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", - "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", - "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", - "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", - "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", - "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", - "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", - "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", - "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", - "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", - "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", - "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", - "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", - "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", - "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", - "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", - "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", - "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", - "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", - "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", - "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", - "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", - "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", - "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", - "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", - "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", - "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", - "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", - "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", - "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", - "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", - "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", - "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", - "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", - "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", - "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", - "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", - "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", - "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", - "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", - "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", - "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", - "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", - "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", - "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", - "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", - "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", - "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87" - ], - "markers": "python_version >= '3.8' and python_full_version not in '3.9.0, 3.9.1'", - "version": "==46.0.5" - }, - "cyclopts": { - "hashes": [ - "sha256:0a891cb55bfd79a3cdce024db8987b33316aba11071e5258c21ac12a640ba9f2", - "sha256:483c4704b953ea6da742e8de15972f405d2e748d19a848a4d61595e8e5360ee5" - ], - "markers": "python_version >= '3.10'", - "version": "==4.6.0" - }, - "diskcache": { - "hashes": [ - "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", - "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19" - ], - "markers": "python_version >= '3'", - "version": "==5.6.3" - }, - "distro": { - "hashes": [ - "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", - "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2" - ], - "markers": "python_version >= '3.6'", - "version": "==1.9.0" - }, - "dnspython": { - "hashes": [ - "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", - "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f" - ], - "markers": "python_version >= '3.10'", - "version": "==2.8.0" - }, - "docstring-parser": { - "hashes": [ - "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", - "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708" - ], - "markers": "python_version >= '3.8'", - "version": "==0.17.0" - }, - "docutils": { - "hashes": [ - "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", - "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de" - ], - "markers": "python_version >= '3.9'", - "version": "==0.22.4" - }, - "email-validator": { - "hashes": [ - "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", - "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426" - ], - "markers": "python_version >= '3.8'", - "version": "==2.3.0" - }, - "exceptiongroup": { - "hashes": [ - "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", - "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.1" - }, - "falkordb": { - "hashes": [ - "sha256:0f190e9d6104595fd51ece4f1e7b5d49d62cfee346d94151d7986a138fd90d89", - "sha256:5c307d973f3fc3987a18478ebd5882f7e842d4225463a8ef5e026970ebfba8c6" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==1.6.0" - }, - "fastapi": { - "hashes": [ - "sha256:31e2ddc78d6406c6f7d5d7b9996a057985e2600fbe7e9ba6ace8205d48dff688", - "sha256:bd37903acf014d1284bda027096e460814dca9699f9dacfe11c275749d949f4d" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==0.135.0" - }, - "fastmcp": { - "hashes": [ - "sha256:6bd73b4a3bab773ee6932df5249dcbcd78ed18365ed0aeeb97bb42702a7198d7", - "sha256:f513d80d4b30b54749fe8950116b1aab843f3c293f5cb971fc8665cb48dbb028" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==3.0.2" - }, - "fastuuid": { - "hashes": [ - "sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1", - "sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede", - "sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11", - "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", - "sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc", - "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", - "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", - "sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7", - "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", - "sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b", - "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", - "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", - "sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4", - "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", - "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", - "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", - "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", - "sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94", - "sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36", - "sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346", - "sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4", - "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", - "sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3", - "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", - "sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4", - "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", - "sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3", - "sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea", - "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", - "sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722", - "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", - "sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0", - "sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85", - "sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34", - "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", - "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", - "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", - "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", - "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", - "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", - "sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c", - "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", - "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", - "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", - "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", - "sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24", - "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", - "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", - "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", - "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", - "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", - "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", - "sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209", - "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", - "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", - "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", - "sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8", - "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", - "sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73", - "sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836", - "sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8", - "sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779", - "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", - "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", - "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", - "sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7", - "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", - "sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105", - "sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173", - "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", - "sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505", - "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", - "sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06", - "sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa", - "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", - "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", - "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", - "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d" - ], - "markers": "python_version >= '3.8'", - "version": "==0.14.0" - }, - "filelock": { - "hashes": [ - "sha256:5ccf8069f7948f494968fc0713c10e5c182a9c9d9eef3a636307a20c2490f047", - "sha256:8f00faf3abf9dc730a1ffe9c354ae5c04e079ab7d3a683b7c32da5dd05f26af3" - ], - "markers": "python_version >= '3.10'", - "version": "==3.25.0" - }, - "frozenlist": { - "hashes": [ - "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", - "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", - "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", - "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", - "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", - "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", - "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", - "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", - "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", - "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", - "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", - "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", - "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", - "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", - "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", - "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", - "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", - "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", - "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", - "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", - "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", - "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", - "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", - "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", - "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", - "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", - "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", - "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", - "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", - "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", - "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", - "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", - "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", - "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", - "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", - "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", - "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", - "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", - "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", - "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", - "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", - "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", - "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", - "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", - "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", - "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", - "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", - "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", - "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", - "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", - "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", - "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", - "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", - "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", - "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", - "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", - "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", - "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", - "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", - "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", - "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", - "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", - "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", - "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", - "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", - "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", - "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", - "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", - "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", - "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", - "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", - "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", - "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", - "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", - "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", - "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", - "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", - "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", - "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", - "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", - "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", - "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", - "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", - "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", - "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", - "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", - "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", - "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", - "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", - "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", - "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", - "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", - "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", - "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", - "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", - "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", - "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", - "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", - "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", - "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", - "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", - "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", - "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", - "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", - "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", - "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", - "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", - "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", - "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", - "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", - "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", - "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", - "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", - "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", - "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", - "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", - "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", - "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", - "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", - "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", - "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", - "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", - "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", - "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", - "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", - "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", - "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", - "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", - "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", - "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd" - ], - "markers": "python_version >= '3.9'", - "version": "==1.8.0" - }, - "fsspec": { - "hashes": [ - "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", - "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437" - ], - "markers": "python_version >= '3.10'", - "version": "==2026.2.0" - }, - "graphiti-core": { - "git": "git+https://github.com/FalkorDB/graphiti.git", - "markers": "python_version >= '3.10' and python_version < '4'", - "ref": "6e3c54ae2891e10a24d698a2f0733d65d148edf4" - }, - "h11": { - "hashes": [ - "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", - "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" - ], - "markers": "python_version >= '3.8'", - "version": "==0.16.0" - }, - "hf-xet": { - "hashes": [ - "sha256:06b724a361f670ae557836e57801b82c75b534812e351a87a2c739f77d1e0635", - "sha256:06cdbde243c85f39a63b28e9034321399c507bcd5e7befdd17ed2ccc06dfe14e", - "sha256:1c88fbd90ad0d27c46b77a445f0a436ebaa94e14965c581123b68b1c52f5fd30", - "sha256:211f30098512d95e85ad03ae63bd7dd2c4df476558a5095d09f9e38e78cbf674", - "sha256:305f5489d7241a47e0458ef49334be02411d1d0f480846363c1c8084ed9916f7", - "sha256:3155a02e083aa21fd733a7485c7c36025e49d5975c8d6bda0453d224dd0b0ac4", - "sha256:31612ba0629046e425ba50375685a2586e11fb9144270ebabd75878c3eaf6378", - "sha256:335a8f36c55fd35a92d0062f4e9201b4015057e62747b7e7001ffb203c0ee1d2", - "sha256:35b855024ca37f2dd113ac1c08993e997fbe167b9d61f9ef66d3d4f84015e508", - "sha256:433c77c9f4e132b562f37d66c9b22c05b5479f243a1f06a120c1c06ce8b1502a", - "sha256:4a6817c41de7c48ed9270da0b02849347e089c5ece9a0e72ae4f4b3a57617f82", - "sha256:4bc995d6c41992831f762096020dc14a65fdf3963f86ffed580b596d04de32e3", - "sha256:7c2a054a97c44e136b1f7f5a78f12b3efffdf2eed3abc6746fc5ea4b39511633", - "sha256:83d8ec273136171431833a6957e8f3af496bee227a0fe47c7b8b39c106d1749a", - "sha256:91b1dc03c31cbf733d35dc03df7c5353686233d86af045e716f1e0ea4a2673cf", - "sha256:9298b47cce6037b7045ae41482e703c471ce36b52e73e49f71226d2e8e5685a1", - "sha256:959083c89dee30f7d6f890b36cdadda823386c4de63b1a30384a75bfd2ae995d", - "sha256:a85d3d43743174393afe27835bde0cd146e652b5fcfdbcd624602daef2ef3259", - "sha256:c1980abfb68ecf6c1c7983379ed7b1e2b49a1aaf1a5aca9acc7d48e5e2e0a961", - "sha256:c1ae4d3a716afc774e66922f3cac8206bfa707db13f6a7e62dfff74bfc95c9a8", - "sha256:c34e2c7aefad15792d57067c1c89b2b02c1bbaeabd7f8456ae3d07b4bbaf4094", - "sha256:cfa760888633b08c01b398d212ce7e8c0d7adac6c86e4b20dfb2397d8acd78ee", - "sha256:d6dbdf231efac0b9b39adcf12a07f0c030498f9212a18e8c50224d0e84ab803d", - "sha256:e130ee08984783d12717444e538587fa2119385e5bd8fc2bb9f930419b73a7af", - "sha256:f93b7595f1d8fefddfede775c18b5c9256757824f7f6832930b49858483cd56f" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.2" - }, - "httpcore": { - "hashes": [ - "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", - "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8" - ], - "markers": "python_version >= '3.8'", - "version": "==1.0.9" - }, - "httpx": { - "hashes": [ - "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", - "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" - ], - "markers": "python_version >= '3.8'", - "version": "==0.28.1" - }, - "httpx-sse": { - "hashes": [ - "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", - "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d" - ], - "markers": "python_version >= '3.9'", - "version": "==0.4.3" - }, - "huggingface-hub": { - "hashes": [ - "sha256:c9c0b3ab95a777fc91666111f3b3ede71c0cdced3614c553a64e98920585c4ee", - "sha256:f281838db29265880fb543de7a23b0f81d3504675de82044307ea3c6c62f799d" - ], - "markers": "python_full_version >= '3.9.0'", - "version": "==1.5.0" - }, - "idna": { - "hashes": [ - "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", - "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" - ], - "markers": "python_version >= '3.8'", - "version": "==3.11" - }, - "importlib-metadata": { - "hashes": [ - "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", - "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151" - ], - "markers": "python_version >= '3.9'", - "version": "==8.7.1" - }, - "itsdangerous": { - "hashes": [ - "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", - "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.2.0" - }, - "jaraco.classes": { - "hashes": [ - "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", - "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" - ], - "markers": "python_version >= '3.8'", - "version": "==3.4.0" - }, - "jaraco.context": { - "hashes": [ - "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", - "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda" - ], - "markers": "python_version >= '3.9'", - "version": "==6.1.0" - }, - "jaraco.functools": { - "hashes": [ - "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", - "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb" - ], - "markers": "python_version >= '3.9'", - "version": "==4.4.0" - }, - "jeepney": { - "hashes": [ - "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", - "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "jinja2": { - "hashes": [ - "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", - "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.1.6" - }, - "jiter": { - "hashes": [ - "sha256:00203f47c214156df427b5989de74cb340c65c8180d09be1bf9de81d0abad599", - "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", - "sha256:0733312953b909688ae3c2d58d043aa040f9f1a6a75693defed7bc2cc4bf2654", - "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", - "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", - "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", - "sha256:0b34c519e17658ed88d5047999a93547f8889f3c1824120c26ad6be5f27b6cf5", - "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", - "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", - "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", - "sha256:0f0c065695f616a27c920a56ad0d4fc46415ef8b806bf8fc1cacf25002bd24e1", - "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", - "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", - "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", - "sha256:19928b5d1ce0ff8c1ee1b9bdef3b5bfc19e8304f1b904e436caf30bc15dc6cf5", - "sha256:19cd6f85e1dc090277c3ce90a5b7d96f32127681d825e71c9dce28788e39fc0c", - "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", - "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", - "sha256:2113c17c9a67071b0f820733c0893ed1d467b5fcf4414068169e5c2cabddb1e2", - "sha256:24ab43126d5e05f3d53a36a8e11eb2f23304c6c1117844aaaf9a0aa5e40b5018", - "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", - "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", - "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", - "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", - "sha256:2ffc63785fd6c7977defe49b9824ae6ce2b2e2b77ce539bdaf006c26da06342e", - "sha256:309549b778b949d731a2f0e1594a3f805716be704a73bf3ad9a807eed5eb5721", - "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", - "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", - "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", - "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", - "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", - "sha256:4397ee562b9f69d283e5674445551b47a5e8076fdde75e71bfac5891113dc543", - "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434", - "sha256:47455245307e4debf2ce6c6e65a717550a0244231240dcf3b8f7d64e4c2f22f4", - "sha256:4a638816427006c1e3f0013eb66d391d7a3acda99a7b0cf091eff4497ccea33a", - "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", - "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", - "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d", - "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", - "sha256:5d9b34ad56761b3bf0fbe8f7e55468704107608512350962d3317ffd7a4382d5", - "sha256:6207fc61c395b26fffdcf637a0b06b4326f35bfa93c6e92fe1a166a21aeb6731", - "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", - "sha256:66aa3e663840152d18cc8ff1e4faad3dd181373491b9cfdc6004b92198d67911", - "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", - "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", - "sha256:6eeb7db8bc77dc20476bc2f7407a23dbe3d46d9cc664b166e3d474e1c1de4baa", - "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", - "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", - "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", - "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", - "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", - "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", - "sha256:7c26ad6967c9dcedf10c995a21539c3aa57d4abad7001b7a84f621a263a6b605", - "sha256:7f90023f8f672e13ea1819507d2d21b9d2d1c18920a3b3a5f1541955a85b5504", - "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", - "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", - "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", - "sha256:9621ca242547edc16400981ca3231e0c91c0c4c1ab8573a596cd9bb3575d5c2b", - "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", - "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", - "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", - "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", - "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", - "sha256:9da38b4fedde4fb528c740c2564628fbab737166a0e73d6d46cb4bb5463ff411", - "sha256:9ffda299e417dc83362963966c50cb76d42da673ee140de8a8ac762d4bb2378b", - "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", - "sha256:a3a377af27b236abbf665a69b2bdd680e3b5a0bd2af825cd3b81245279a7606c", - "sha256:a576f5dce9ac7de5d350b8e2f552cf364f32975ed84717c35379a51c7cb198bd", - "sha256:a7637d92b1c9d7a771e8c56f445c7f84396d48f2e756e5978840ecba2fac0894", - "sha256:ab1185ca5c8b9491b55ebf6c1e8866b8f68258612899693e24a92c5fdb9455d5", - "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", - "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", - "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", - "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c", - "sha256:b22945be8425d161f2e536cdae66da300b6b000f1c0ba3ddf237d1bfd45d21b8", - "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", - "sha256:bcdabaea26cb04e25df3103ce47f97466627999260290349a88c8136ecae0060", - "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", - "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", - "sha256:c1b609e5cbd2f52bb74fb721515745b407df26d7b800458bd97cb3b972c29e7d", - "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", - "sha256:c3524798e70655ff19aec58c7d05adb1f074fecff62da857ea9be2b908b6d701", - "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", - "sha256:d2a6394e6af690d462310a86b53c47ad75ac8c21dc79f120714ea449979cb1d3", - "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2", - "sha256:dc3ce84cfd4fa9628fe62c4f85d0d597a4627d4242cfafac32a12cc1455d00f7", - "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", - "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", - "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", - "sha256:ea026e70a9a28ebbdddcbcf0f1323128a8db66898a06eaad3a4e62d2f554d096", - "sha256:ec7e287d7fbd02cb6e22f9a00dd9c9cd504c40a61f2c61e7e1f9690a82726b4c", - "sha256:ed0240dd1536a98c3ab55e929c60dfff7c899fecafcb7d01161b21a99fc8c363", - "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", - "sha256:ee9da221dca6e0429c2704c1b3655fe7b025204a71d4d9b73390c759d776d165", - "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", - "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", - "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", - "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", - "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", - "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", - "sha256:fe49d3ff6db74321f144dff9addd4a5874d3105ac5ba7c5b77fac099cfae31ae", - "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f" - ], - "markers": "python_version >= '3.9'", - "version": "==0.13.0" - }, - "jsonref": { - "hashes": [ - "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", - "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9" - ], - "markers": "python_version >= '3.7'", - "version": "==1.1.0" - }, - "jsonschema": { - "hashes": [ - "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", - "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==4.26.0" - }, - "jsonschema-path": { - "hashes": [ - "sha256:5f5ff183150030ea24bb51cf1ddac9bf5dbf030272e2792a7ffe8262f7eea2a5", - "sha256:9c3d88e727cc4f1a88e51dbbed4211dbcd815d27799d2685efd904435c3d39e7" - ], - "markers": "python_version >= '3.10' and python_full_version < '4.0.0'", - "version": "==0.4.2" - }, - "jsonschema-specifications": { - "hashes": [ - "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", - "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d" - ], - "markers": "python_version >= '3.9'", - "version": "==2025.9.1" - }, - "keyring": { - "hashes": [ - "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", - "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b" - ], - "markers": "python_version >= '3.9'", - "version": "==25.7.0" - }, - "litellm": { - "hashes": [ - "sha256:5496b5d4532cccdc7a095c21cbac4042f7662021c57bc1d17be4e39838929e80", - "sha256:d388f52447daccbcaafa19a3e68d17b75f1374b5bf2cde680d65e1cd86e50d22" - ], - "index": "pypi", - "markers": "python_version >= '3.9' and python_version < '4.0'", - "version": "==1.82.0" - }, - "markdown-it-py": { - "hashes": [ - "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", - "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" - ], - "markers": "python_version >= '3.10'", - "version": "==4.0.0" - }, - "markupsafe": { - "hashes": [ - "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", - "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", - "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", - "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", - "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", - "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", - "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", - "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", - "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", - "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", - "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", - "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", - "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", - "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", - "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", - "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", - "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", - "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", - "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", - "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", - "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", - "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", - "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", - "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", - "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", - "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", - "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", - "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", - "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", - "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", - "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", - "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", - "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", - "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", - "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", - "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", - "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", - "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", - "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", - "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", - "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", - "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", - "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", - "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", - "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", - "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", - "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", - "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", - "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", - "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", - "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", - "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", - "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", - "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", - "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", - "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", - "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", - "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", - "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", - "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", - "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", - "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", - "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", - "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", - "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", - "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", - "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", - "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", - "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", - "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", - "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", - "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", - "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", - "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", - "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", - "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", - "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", - "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", - "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", - "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", - "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", - "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", - "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", - "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", - "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", - "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", - "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", - "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", - "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50" - ], - "markers": "python_version >= '3.9'", - "version": "==3.0.3" - }, - "mcp": { - "hashes": [ - "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", - "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66" - ], - "markers": "python_version >= '3.10'", - "version": "==1.26.0" - }, - "mdurl": { - "hashes": [ - "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", - "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" - ], - "markers": "python_version >= '3.7'", - "version": "==0.1.2" - }, - "more-itertools": { - "hashes": [ - "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", - "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd" - ], - "markers": "python_version >= '3.9'", - "version": "==10.8.0" - }, - "multidict": { - "hashes": [ - "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", - "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", - "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", - "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", - "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", - "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", - "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", - "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", - "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", - "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", - "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", - "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", - "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", - "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", - "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", - "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", - "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", - "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", - "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", - "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", - "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", - "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", - "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", - "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", - "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", - "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", - "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", - "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", - "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", - "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", - "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", - "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", - "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", - "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", - "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", - "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", - "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", - "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", - "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", - "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", - "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", - "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", - "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", - "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", - "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", - "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", - "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", - "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", - "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", - "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", - "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", - "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", - "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", - "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", - "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", - "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", - "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", - "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", - "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", - "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", - "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", - "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", - "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", - "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", - "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", - "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", - "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", - "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", - "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", - "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", - "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", - "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", - "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", - "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", - "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", - "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", - "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", - "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", - "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", - "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", - "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", - "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", - "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", - "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", - "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", - "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", - "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", - "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", - "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", - "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", - "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", - "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", - "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", - "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", - "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", - "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", - "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", - "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", - "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", - "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", - "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", - "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", - "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", - "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", - "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", - "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", - "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", - "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", - "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", - "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", - "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", - "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", - "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", - "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", - "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", - "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", - "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", - "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", - "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", - "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", - "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", - "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", - "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", - "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", - "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", - "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", - "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", - "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", - "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", - "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", - "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", - "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", - "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", - "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", - "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", - "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", - "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", - "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", - "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", - "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", - "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", - "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", - "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", - "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", - "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", - "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19" - ], - "markers": "python_version >= '3.9'", - "version": "==6.7.1" - }, - "neo4j": { - "hashes": [ - "sha256:3bd93941f3a3559af197031157220af9fd71f4f93a311db687bd69ffa417b67d", - "sha256:b5dde8c0d8481e7b6ae3733569d990dd3e5befdc5d452f531ad1884ed3500b84" - ], - "markers": "python_version >= '3.10'", - "version": "==6.1.0" - }, - "numpy": { - "hashes": [ - "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", - "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", - "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", - "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", - "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", - "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", - "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", - "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", - "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", - "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", - "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", - "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", - "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", - "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", - "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", - "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", - "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", - "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", - "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", - "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", - "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", - "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", - "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", - "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", - "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", - "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", - "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", - "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", - "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", - "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", - "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", - "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", - "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", - "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", - "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", - "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", - "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", - "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", - "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", - "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", - "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", - "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", - "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", - "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", - "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", - "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", - "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", - "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", - "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", - "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", - "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", - "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", - "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", - "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", - "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", - "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", - "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", - "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", - "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", - "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", - "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", - "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", - "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", - "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", - "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", - "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", - "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", - "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", - "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", - "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", - "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", - "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1" - ], - "markers": "python_version >= '3.11'", - "version": "==2.4.2" - }, - "openai": { - "hashes": [ - "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673", - "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94" - ], - "markers": "python_version >= '3.9'", - "version": "==2.24.0" - }, - "openapi-pydantic": { - "hashes": [ - "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", - "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d" - ], - "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==0.5.1" - }, - "opentelemetry-api": { - "hashes": [ - "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", - "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c" - ], - "markers": "python_version >= '3.9'", - "version": "==1.39.1" - }, - "packaging": { - "hashes": [ - "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", - "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529" - ], - "markers": "python_version >= '3.8'", - "version": "==26.0" - }, - "pathable": { - "hashes": [ - "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6", - "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1" - ], - "markers": "python_version >= '3.10' and python_version < '4.0'", - "version": "==0.5.0" - }, - "platformdirs": { - "hashes": [ - "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", - "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291" - ], - "markers": "python_version >= '3.10'", - "version": "==4.9.2" - }, - "posthog": { - "hashes": [ - "sha256:2ddcacdef6c4afb124ebfcf27d7be58388943a7e24f8d4a51a52732c9b90bad6", - "sha256:55f7580265d290936ac4c112a4e2031a41743be4f90d4183ac9f85b721ff13ae" - ], - "markers": "python_version >= '3.10'", - "version": "==7.9.3" - }, - "propcache": { - "hashes": [ - "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", - "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", - "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", - "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", - "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", - "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", - "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", - "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", - "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", - "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", - "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", - "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", - "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", - "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", - "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", - "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", - "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", - "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", - "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", - "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", - "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", - "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", - "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", - "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", - "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", - "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", - "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", - "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", - "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", - "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", - "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", - "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", - "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", - "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", - "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", - "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", - "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", - "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", - "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", - "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", - "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", - "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", - "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", - "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", - "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", - "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", - "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", - "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", - "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", - "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", - "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", - "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", - "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", - "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", - "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", - "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", - "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", - "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", - "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", - "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", - "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", - "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", - "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", - "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", - "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", - "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", - "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", - "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", - "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", - "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", - "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", - "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", - "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", - "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", - "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", - "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", - "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", - "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", - "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", - "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", - "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", - "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", - "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", - "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", - "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", - "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", - "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", - "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", - "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", - "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", - "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", - "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", - "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", - "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", - "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", - "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", - "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", - "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", - "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", - "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", - "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", - "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", - "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", - "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", - "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", - "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", - "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", - "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", - "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", - "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", - "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", - "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", - "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", - "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", - "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", - "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", - "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", - "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", - "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", - "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", - "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", - "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781" - ], - "markers": "python_version >= '3.9'", - "version": "==0.4.1" - }, - "psycopg2-binary": { - "hashes": [ - "sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f", - "sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1", - "sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152", - "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10", - "sha256:20e7fb94e20b03dcc783f76c0865f9da39559dcc0c28dd1a3fce0d01902a6b9c", - "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee", - "sha256:2d11098a83cca92deaeaed3d58cfd150d49b3b06ee0d0852be466bf87596899e", - "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4", - "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03", - "sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a", - "sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b", - "sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee", - "sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e", - "sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316", - "sha256:41360b01c140c2a03d346cec3280cf8a71aa07d94f3b1509fa0161c366af66b4", - "sha256:44fc5c2b8fa871ce7f0023f619f1349a0aa03a0857f2c96fbc01c657dcbbdb49", - "sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c", - "sha256:4bdab48575b6f870f465b397c38f1b415520e9879fdf10a53ee4f49dcbdf8a21", - "sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b", - "sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3", - "sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b", - "sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d", - "sha256:691c807d94aecfbc76a14e1408847d59ff5b5906a04a23e12a89007672b9e819", - "sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a", - "sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f", - "sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14", - "sha256:875039274f8a2361e5207857899706da840768e2a775bf8c65e82f60b197df02", - "sha256:8b81627b691f29c4c30a8f322546ad039c40c328373b11dff7490a3e1b517855", - "sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0", - "sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd", - "sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1", - "sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5", - "sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f", - "sha256:9c55460033867b4622cda1b6872edf445809535144152e5d14941ef591980edf", - "sha256:9d3a9edcfbe77a3ed4bc72836d466dfce4174beb79eda79ea155cc77237ed9e8", - "sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757", - "sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2", - "sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb", - "sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087", - "sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a", - "sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c", - "sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d", - "sha256:b637d6d941209e8d96a072d7977238eea128046effbf37d1d8b2c0764750017d", - "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c", - "sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c", - "sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4", - "sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4", - "sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e", - "sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766", - "sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d", - "sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d", - "sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39", - "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908", - "sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60", - "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7", - "sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2", - "sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8", - "sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f", - "sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f", - "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f", - "sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34", - "sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3", - "sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa", - "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94", - "sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc", - "sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db", - "sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==2.9.11" - }, - "py-key-value-aio": { - "extras": [ - "filetree", - "keyring", - "memory" - ], - "hashes": [ - "sha256:18e17564ecae61b987f909fc2cd41ee2012c84b4b1dcb8c055cf8b4bc1bf3f5d", - "sha256:e3012e6243ed7cc09bb05457bd4d03b1ba5c2b1ca8700096b3927db79ffbbe55" - ], - "markers": "python_version >= '3.10'", - "version": "==0.4.4" - }, - "pycparser": { - "hashes": [ - "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", - "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992" - ], - "markers": "python_version >= '3.10'", - "version": "==3.0" - }, - "pydantic": { - "extras": [ - "email" - ], - "hashes": [ - "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", - "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d" - ], - "markers": "python_version >= '3.9'", - "version": "==2.12.5" - }, - "pydantic-core": { - "hashes": [ - "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", - "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", - "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", - "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", - "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", - "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", - "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", - "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", - "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", - "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", - "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", - "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", - "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", - "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", - "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", - "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", - "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", - "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", - "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", - "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", - "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", - "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", - "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", - "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", - "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", - "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", - "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", - "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", - "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", - "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", - "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", - "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", - "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", - "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", - "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", - "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", - "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", - "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", - "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", - "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", - "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", - "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", - "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", - "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", - "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", - "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", - "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", - "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", - "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", - "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", - "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", - "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", - "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", - "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", - "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", - "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", - "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", - "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", - "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", - "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", - "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", - "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", - "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", - "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", - "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", - "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", - "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", - "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", - "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", - "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", - "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", - "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", - "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", - "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", - "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", - "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", - "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", - "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", - "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", - "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", - "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", - "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", - "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", - "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", - "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", - "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", - "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", - "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", - "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", - "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", - "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", - "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", - "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", - "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", - "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", - "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", - "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", - "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", - "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", - "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", - "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", - "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", - "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", - "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", - "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", - "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", - "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", - "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", - "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", - "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", - "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", - "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", - "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", - "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", - "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", - "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", - "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", - "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", - "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", - "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", - "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52" - ], - "markers": "python_version >= '3.9'", - "version": "==2.41.5" - }, - "pydantic-settings": { - "hashes": [ - "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", - "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237" - ], - "markers": "python_version >= '3.10'", - "version": "==2.13.1" - }, - "pygments": { - "hashes": [ - "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", - "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" - ], - "markers": "python_version >= '3.8'", - "version": "==2.19.2" - }, - "pyjwt": { - "extras": [ - "crypto" - ], - "hashes": [ - "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", - "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469" - ], - "markers": "python_version >= '3.9'", - "version": "==2.11.0" - }, - "pymysql": { - "hashes": [ - "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", - "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.1.2" - }, - "pyperclip": { - "hashes": [ - "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", - "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273" - ], - "version": "==1.11.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", - "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==2.9.0.post0" - }, - "python-dotenv": { - "hashes": [ - "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", - "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3" - ], - "markers": "python_version >= '3.10'", - "version": "==1.2.2" - }, - "python-multipart": { - "hashes": [ - "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", - "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==0.0.22" - }, - "pytz": { - "hashes": [ - "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", - "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" - ], - "version": "==2025.2" - }, - "pyyaml": { - "hashes": [ - "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", - "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", - "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", - "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", - "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", - "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", - "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", - "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", - "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", - "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", - "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", - "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6", - "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", - "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", - "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", - "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", - "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", - "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", - "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295", - "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", - "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", - "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", - "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", - "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", - "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", - "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", - "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", - "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b", - "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", - "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", - "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", - "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", - "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369", - "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", - "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", - "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", - "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", - "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", - "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", - "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", - "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", - "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", - "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", - "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", - "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", - "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", - "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", - "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", - "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", - "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4", - "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", - "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", - "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", - "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", - "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", - "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", - "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", - "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", - "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", - "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", - "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", - "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f", - "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", - "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", - "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", - "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", - "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", - "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", - "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", - "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3", - "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", - "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", - "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" - ], - "markers": "python_version >= '3.8'", - "version": "==6.0.3" - }, - "redis": { - "hashes": [ - "sha256:01f591f8598e483f1842d429e8ae3a820804566f1c73dca1b80e23af9fba0497", - "sha256:4dd5bf4bd4ae80510267f14185a15cba2a38666b941aff68cccf0256b51c1f26" - ], - "markers": "python_version >= '3.10'", - "version": "==7.2.0" - }, - "referencing": { - "hashes": [ - "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", - "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8" - ], - "markers": "python_version >= '3.10'", - "version": "==0.37.0" - }, - "regex": { - "hashes": [ - "sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1", - "sha256:00f2b8d9615aa165fdff0a13f1a92049bfad555ee91e20d246a51aa0b556c60a", - "sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4", - "sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d", - "sha256:03a83cc26aa2acda6b8b9dfe748cf9e84cbd390c424a1de34fdcef58961a297a", - "sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911", - "sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952", - "sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b", - "sha256:0d5bef2031cbf38757a0b0bc4298bb4824b6332d28edc16b39247228fbdbad97", - "sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25", - "sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8", - "sha256:195237dc327858a7721bf8b0bbbef797554bc13563c3591e91cd0767bacbe359", - "sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff", - "sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a", - "sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7", - "sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0", - "sha256:1f8b17be5c27a684ea6759983c13506bd77bfc7c0347dff41b18ce5ddd2ee09a", - "sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215", - "sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43", - "sha256:2954379dd20752e82d22accf3ff465311cbb2bac6c1f92c4afd400e1757f7451", - "sha256:2afa673660928d0b63d84353c6c08a8a476ddfc4a47e11742949d182e6863ce8", - "sha256:2b2b23587b26496ff5fd40df4278becdf386813ec00dc3533fa43a4cf0e2ad3c", - "sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b", - "sha256:3935174fa4d9f70525a4367aaff3cb8bc0548129d114260c29d9dfa4a5b41692", - "sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e", - "sha256:3b24bd7e9d85dc7c6a8bd2aa14ecd234274a0248335a02adeb25448aecdd420d", - "sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae", - "sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8", - "sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11", - "sha256:50c2fc924749543e0eacc93ada6aeeb3ea5f6715825624baa0dccaec771668ae", - "sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5", - "sha256:516604edd17b1c2c3e579cf4e9b25a53bf8fa6e7cedddf1127804d3e0140ca64", - "sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472", - "sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18", - "sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a", - "sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd", - "sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d", - "sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d", - "sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9", - "sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96", - "sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784", - "sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b", - "sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff", - "sha256:73cdcdbba8028167ea81490c7f45280113e41db2c7afb65a276f4711fa3bcbff", - "sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc", - "sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf", - "sha256:7ab218076eb0944549e7fe74cf0e2b83a82edb27e81cc87411f76240865e04d5", - "sha256:7c1b34dfa72f826f535b20712afa9bb3ba580020e834f3c69866c5bddbf10098", - "sha256:851fa70df44325e1e4cdb79c5e676e91a78147b1b543db2aec8734d2add30ec2", - "sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05", - "sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf", - "sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6", - "sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768", - "sha256:948c12ef30ecedb128903c2c2678b339746eb7c689c5c21957c4a23950c96d15", - "sha256:94d63db12e45a9b9f064bfe4800cefefc7e5f182052e4c1b774d46a40ab1d9bb", - "sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881", - "sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f", - "sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8", - "sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c", - "sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d", - "sha256:9dd450db6458387167e033cfa80887a34c99c81d26da1bf8b0b41bf8c9cac88e", - "sha256:a25c7701e4f7a70021db9aaf4a4a0a67033c6318752146e03d1b94d32006217e", - "sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341", - "sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e", - "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", - "sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550", - "sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e", - "sha256:b387a0d092dac157fb026d737dde35ff3e49ef27f285343e7c6401851239df27", - "sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8", - "sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59", - "sha256:b49eb78048c6354f49e91e4b77da21257fecb92256b6d599ae44403cab30b05b", - "sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3", - "sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117", - "sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc", - "sha256:ba55c50f408fb5c346a3a02d2ce0ebc839784e24f7c9684fde328ff063c3cdea", - "sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b", - "sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e", - "sha256:bcb399ed84eabf4282587ba151f2732ad8168e66f1d3f85b1d038868fe547703", - "sha256:bd477d5f79920338107f04aa645f094032d9e3030cc55be581df3d1ef61aa318", - "sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2", - "sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952", - "sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944", - "sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7", - "sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b", - "sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033", - "sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4", - "sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8", - "sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f", - "sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d", - "sha256:dd8847c4978bc3c7e6c826fb745f5570e518b8459ac2892151ce6627c7bc00d5", - "sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d", - "sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec", - "sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b", - "sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a", - "sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92", - "sha256:e621fb7c8dc147419b28e1702f58a0177ff8308a76fa295c71f3e7827849f5d9", - "sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc", - "sha256:e7ce83654d1ab701cb619285a18a8e5a889c1216d746ddc710c914ca5fd71022", - "sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6", - "sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c", - "sha256:ec6f5674c5dc836994f50f1186dd1fafde4be0666aae201ae2fcc3d29d8adf27", - "sha256:edb1b1b3a5576c56f08ac46f108c40333f222ebfd5cf63afdfa3aab0791ebe5b", - "sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc", - "sha256:f2791948f7c70bb9335a9102df45e93d428f4b8128020d85920223925d73b9e1", - "sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07", - "sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c", - "sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a", - "sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33", - "sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95", - "sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081", - "sha256:fc48c500838be6882b32748f60a15229d2dea96e59ef341eaa96ec83538f498d", - "sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7", - "sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb", - "sha256:fd63453f10d29097cc3dc62d070746523973fb5aa1c66d25f8558bebd47fed61" - ], - "markers": "python_version >= '3.10'", - "version": "==2026.2.28" - }, - "requests": { - "hashes": [ - "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", - "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" - ], - "markers": "python_version >= '3.9'", - "version": "==2.32.5" - }, - "rich": { - "hashes": [ - "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", - "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==14.3.3" - }, - "rich-rst": { - "hashes": [ - "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", - "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a" - ], - "version": "==1.3.2" - }, - "rpds-py": { - "hashes": [ - "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", - "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", - "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", - "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", - "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", - "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", - "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", - "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", - "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", - "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", - "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", - "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", - "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", - "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", - "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", - "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", - "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", - "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", - "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", - "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", - "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", - "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", - "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", - "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", - "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", - "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", - "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", - "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", - "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", - "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", - "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", - "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", - "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", - "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", - "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", - "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", - "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", - "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", - "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", - "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", - "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", - "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", - "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", - "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", - "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", - "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", - "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", - "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", - "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", - "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", - "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", - "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", - "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", - "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", - "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", - "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", - "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", - "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", - "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", - "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", - "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", - "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", - "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", - "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", - "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", - "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", - "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", - "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", - "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", - "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", - "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", - "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", - "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", - "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", - "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", - "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", - "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", - "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", - "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", - "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", - "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", - "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", - "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", - "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", - "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", - "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", - "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", - "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", - "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", - "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", - "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", - "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", - "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", - "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", - "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", - "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", - "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", - "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", - "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", - "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", - "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", - "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", - "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", - "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", - "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", - "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", - "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", - "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", - "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", - "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", - "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", - "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", - "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", - "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", - "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5" - ], - "markers": "python_version >= '3.10'", - "version": "==0.30.0" - }, - "secretstorage": { - "hashes": [ - "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", - "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be" - ], - "markers": "python_version >= '3.10'", - "version": "==3.5.0" - }, - "shellingham": { - "hashes": [ - "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", - "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.4" - }, - "six": { - "hashes": [ - "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", - "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.17.0" - }, - "sniffio": { - "hashes": [ - "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", - "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.1" - }, - "sse-starlette": { - "hashes": [ - "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", - "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422" - ], - "markers": "python_version >= '3.9'", - "version": "==3.2.0" - }, - "starlette": { - "hashes": [ - "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", - "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933" - ], - "markers": "python_version >= '3.10'", - "version": "==0.52.1" - }, - "tenacity": { - "hashes": [ - "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", - "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a" - ], - "markers": "python_version >= '3.10'", - "version": "==9.1.4" - }, - "tiktoken": { - "hashes": [ - "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", - "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", - "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", - "sha256:09eb4eae62ae7e4c62364d9ec3a57c62eea707ac9a2b2c5d6bd05de6724ea179", - "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", - "sha256:15d875454bbaa3728be39880ddd11a5a2a9e548c29418b41e8fd8a767172b5ec", - "sha256:20cf97135c9a50de0b157879c3c4accbb29116bcf001283d26e073ff3b345946", - "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", - "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", - "sha256:2cff3688ba3c639ebe816f8d58ffbbb0aa7433e23e08ab1cade5d175fc973fb3", - "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", - "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", - "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970", - "sha256:3e68e3e593637b53e56f7237be560f7a394451cb8c11079755e80ae64b9e6def", - "sha256:47a5bc270b8c3db00bb46ece01ef34ad050e364b51d406b6f9730b64ac28eded", - "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", - "sha256:4c9614597ac94bb294544345ad8cf30dac2129c05e2db8dc53e082f355857af7", - "sha256:508fa71810c0efdcd1b898fda574889ee62852989f7c1667414736bcb2b9a4bd", - "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", - "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", - "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", - "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", - "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", - "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134", - "sha256:6e227c7f96925003487c33b1b32265fad2fbcec2b7cf4817afb76d416f40f6bb", - "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a", - "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1", - "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", - "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892", - "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", - "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", - "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", - "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", - "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", - "sha256:a1af81a6c44f008cba48494089dd98cccb8b313f55e961a52f5b222d1e507967", - "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", - "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", - "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", - "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16", - "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", - "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", - "sha256:c06cf0fcc24c2cb2adb5e185c7082a82cba29c17575e828518c2f11a01f445aa", - "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", - "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", - "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030", - "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", - "sha256:d51d75a5bffbf26f86554d28e78bfb921eae998edc2675650fd04c7e1f0cdc1e", - "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", - "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", - "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", - "sha256:df37684ace87d10895acb44b7f447d4700349b12197a526da0d4a4149fde074c", - "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", - "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", - "sha256:f18f249b041851954217e9fd8e5c00b024ab2315ffda5ed77665a05fa91f42dc", - "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", - "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", - "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd" - ], - "markers": "python_version >= '3.9'", - "version": "==0.12.0" - }, - "tokenizers": { - "hashes": [ - "sha256:143b999bdc46d10febb15cbffb4207ddd1f410e2c755857b5a0797961bbdc113", - "sha256:1a62ba2c5faa2dd175aaeed7b15abf18d20266189fb3406c5d0550dd34dd5f37", - "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", - "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", - "sha256:1e50f8554d504f617d9e9d6e4c2c2884a12b388a97c5c77f0bc6cf4cd032feee", - "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", - "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", - "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", - "sha256:319f659ee992222f04e58f84cbf407cfa66a65fe3a8de44e8ad2bc53e7d99012", - "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", - "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", - "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", - "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", - "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", - "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", - "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", - "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", - "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", - "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", - "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", - "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", - "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", - "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", - "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5" - ], - "markers": "python_version >= '3.9'", - "version": "==0.22.2" - }, - "tqdm": { - "hashes": [ - "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", - "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.67.3" - }, - "typer": { - "hashes": [ - "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", - "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45" - ], - "markers": "python_version >= '3.10'", - "version": "==0.24.1" - }, - "typer-slim": { - "hashes": [ - "sha256:d5d7ee1ee2834d5020c7c616ed5e0d0f29b9a4b1dd283bdebae198ec09778d0e", - "sha256:f0ed36127183f52ae6ced2ecb2521789995992c521a46083bfcdbb652d22ad34" - ], - "markers": "python_version >= '3.10'", - "version": "==0.24.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", - "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" - ], - "markers": "python_version >= '3.9'", - "version": "==4.15.0" - }, - "typing-inspection": { - "hashes": [ - "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", - "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464" - ], - "markers": "python_version >= '3.9'", - "version": "==0.4.2" - }, - "urllib3": { - "hashes": [ - "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", - "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4" - ], - "markers": "python_version >= '3.9'", - "version": "==2.6.3" - }, - "uvicorn": { - "hashes": [ - "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", - "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==0.41.0" - }, - "watchfiles": { - "hashes": [ - "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", - "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", - "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", - "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", - "sha256:08af70fd77eee58549cd69c25055dc344f918d992ff626068242259f98d598a2", - "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", - "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", - "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", - "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", - "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", - "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", - "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", - "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", - "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", - "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", - "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", - "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", - "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", - "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", - "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", - "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", - "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", - "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", - "sha256:3dbd8cbadd46984f802f6d479b7e3afa86c42d13e8f0f322d669d79722c8ec34", - "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", - "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", - "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", - "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", - "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", - "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", - "sha256:4b943d3668d61cfa528eb949577479d3b077fd25fb83c641235437bc0b5bc60e", - "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", - "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", - "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", - "sha256:5524298e3827105b61951a29c3512deb9578586abf3a7c5da4a8069df247cccc", - "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", - "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", - "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", - "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", - "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", - "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", - "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", - "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", - "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", - "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", - "sha256:6c3631058c37e4a0ec440bf583bc53cdbd13e5661bb6f465bc1d88ee9a0a4d02", - "sha256:6c9c9262f454d1c4d8aaa7050121eb4f3aea197360553699520767daebf2180b", - "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", - "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", - "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", - "sha256:74472234c8370669850e1c312490f6026d132ca2d396abfad8830b4f1c096957", - "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", - "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", - "sha256:79ff6c6eadf2e3fc0d7786331362e6ef1e51125892c75f1004bd6b52155fb956", - "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", - "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", - "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", - "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", - "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", - "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", - "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", - "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", - "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", - "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", - "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", - "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", - "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", - "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", - "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", - "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", - "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", - "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", - "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", - "sha256:acb08650863767cbc58bca4813b92df4d6c648459dcaa3d4155681962b2aa2d3", - "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", - "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", - "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", - "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", - "sha256:b9c4702f29ca48e023ffd9b7ff6b822acdf47cb1ff44cb490a3f1d5ec8987e9c", - "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", - "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", - "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", - "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", - "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", - "sha256:c1f5210f1b8fc91ead1283c6fd89f70e76fb07283ec738056cf34d51e9c1d62c", - "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", - "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", - "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", - "sha256:c882d69f6903ef6092bedfb7be973d9319940d56b8427ab9187d1ecd73438a70", - "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", - "sha256:cdab464fee731e0884c35ae3588514a9bcf718d0e2c82169c1c4a85cc19c3c7f", - "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", - "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", - "sha256:cf57a27fb986c6243d2ee78392c503826056ffe0287e8794503b10fb51b881be", - "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", - "sha256:d6ff426a7cb54f310d51bfe83fe9f2bbe40d540c741dc974ebc30e6aa238f52e", - "sha256:d7e7067c98040d646982daa1f37a33d3544138ea155536c2e0e63e07ff8a7e0f", - "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", - "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", - "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", - "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", - "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", - "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", - "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", - "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", - "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", - "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", - "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", - "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf" - ], - "markers": "python_version >= '3.9'", - "version": "==1.1.1" - }, - "websockets": { - "hashes": [ - "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", - "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", - "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", - "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", - "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", - "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", - "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", - "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", - "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", - "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", - "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", - "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", - "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", - "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", - "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", - "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", - "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", - "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", - "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", - "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", - "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", - "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", - "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", - "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", - "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", - "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", - "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", - "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", - "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", - "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", - "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", - "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", - "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", - "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", - "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", - "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", - "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", - "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", - "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", - "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", - "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", - "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", - "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", - "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", - "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", - "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", - "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", - "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", - "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", - "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", - "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", - "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", - "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", - "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", - "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", - "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", - "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", - "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", - "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", - "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", - "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4" - ], - "markers": "python_version >= '3.10'", - "version": "==16.0" - }, - "yarl": { - "hashes": [ - "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", - "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", - "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", - "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", - "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", - "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", - "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", - "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", - "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", - "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", - "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", - "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", - "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", - "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", - "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", - "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", - "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", - "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", - "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", - "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", - "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", - "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", - "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", - "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", - "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", - "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", - "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", - "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", - "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", - "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", - "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", - "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", - "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", - "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", - "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", - "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", - "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", - "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", - "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", - "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", - "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", - "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", - "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", - "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", - "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", - "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", - "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", - "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", - "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", - "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", - "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", - "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", - "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", - "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", - "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", - "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", - "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", - "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", - "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", - "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", - "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", - "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", - "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", - "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", - "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", - "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", - "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", - "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", - "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", - "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", - "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", - "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", - "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", - "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", - "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", - "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", - "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", - "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", - "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", - "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", - "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", - "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", - "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", - "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", - "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", - "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", - "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", - "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", - "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", - "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", - "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", - "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", - "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", - "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", - "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", - "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", - "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", - "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", - "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", - "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", - "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", - "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", - "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", - "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", - "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", - "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", - "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", - "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", - "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", - "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", - "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", - "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", - "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", - "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", - "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", - "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", - "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", - "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", - "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", - "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", - "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", - "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", - "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", - "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", - "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", - "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", - "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", - "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d" - ], - "markers": "python_version >= '3.10'", - "version": "==1.23.0" - }, - "zipp": { - "hashes": [ - "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", - "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166" - ], - "markers": "python_version >= '3.9'", - "version": "==3.23.0" - } - }, - "develop": { - "astroid": { - "hashes": [ - "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", - "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0" - ], - "markers": "python_full_version >= '3.10.0'", - "version": "==4.0.4" - }, - "certifi": { - "hashes": [ - "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", - "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7" - ], - "markers": "python_version >= '3.7'", - "version": "==2026.2.25" - }, - "charset-normalizer": { - "hashes": [ - "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", - "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", - "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", - "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", - "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", - "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", - "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63", - "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", - "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", - "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", - "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", - "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", - "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", - "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af", - "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", - "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", - "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", - "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", - "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", - "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", - "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576", - "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", - "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", - "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", - "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", - "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", - "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", - "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", - "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", - "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", - "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", - "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", - "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a", - "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", - "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", - "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", - "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", - "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", - "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7", - "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", - "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", - "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", - "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", - "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", - "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", - "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2", - "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", - "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", - "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", - "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", - "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", - "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", - "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", - "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", - "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa", - "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", - "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", - "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", - "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", - "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", - "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", - "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", - "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", - "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", - "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", - "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", - "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", - "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", - "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", - "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", - "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3", - "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", - "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", - "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", - "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", - "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", - "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", - "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf", - "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", - "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", - "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", - "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", - "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", - "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", - "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", - "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", - "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", - "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4", - "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", - "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", - "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", - "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", - "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", - "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", - "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", - "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", - "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", - "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", - "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074", - "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", - "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", - "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", - "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", - "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d", - "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", - "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", - "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", - "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", - "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", - "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", - "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", - "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", - "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" - ], - "markers": "python_version >= '3.7'", - "version": "==3.4.4" - }, - "dill": { - "hashes": [ - "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", - "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa" - ], - "markers": "python_version >= '3.9'", - "version": "==0.4.1" - }, - "greenlet": { - "hashes": [ - "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd", - "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", - "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", - "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5", - "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", - "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", - "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", - "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", - "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f", - "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327", - "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", - "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", - "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", - "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", - "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", - "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", - "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", - "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", - "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", - "sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f", - "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", - "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", - "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", - "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", - "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", - "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492", - "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", - "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", - "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", - "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", - "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", - "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", - "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", - "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", - "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124", - "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", - "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", - "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", - "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", - "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", - "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", - "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", - "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", - "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", - "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a", - "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", - "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", - "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", - "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71", - "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", - "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643", - "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", - "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9" - ], - "markers": "python_version >= '3.10'", - "version": "==3.3.2" - }, - "idna": { - "hashes": [ - "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", - "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" - ], - "markers": "python_version >= '3.8'", - "version": "==3.11" - }, - "iniconfig": { - "hashes": [ - "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", - "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12" - ], - "markers": "python_version >= '3.10'", - "version": "==2.3.0" - }, - "isort": { - "hashes": [ - "sha256:184916a933041c7cf718787f7e52064f3c06272aff69a5cb4dc46497bd8911d9", - "sha256:fddea59202f231e170e52e71e3510b99c373b6e571b55d9c7b31b679c0fed47c" - ], - "markers": "python_full_version >= '3.10.0'", - "version": "==8.0.0" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "packaging": { - "hashes": [ - "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", - "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529" - ], - "markers": "python_version >= '3.8'", - "version": "==26.0" - }, - "platformdirs": { - "hashes": [ - "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", - "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291" - ], - "markers": "python_version >= '3.10'", - "version": "==4.9.2" - }, - "playwright": { - "hashes": [ - "sha256:185e0132578733d02802dfddfbbc35f42be23a45ff49ccae5081f25952238117", - "sha256:1e03be090e75a0fabbdaeab65ce17c308c425d879fa48bb1d7986f96bfad0b99", - "sha256:32ffe5c303901a13a0ecab91d1c3f74baf73b84f4bedbb6b935f5bc11cc98e1b", - "sha256:70c763694739d28df71ed578b9c8202bb83e8fe8fb9268c04dd13afe36301f71", - "sha256:8f9999948f1ab541d98812de25e3a8c410776aa516d948807140aff797b4bffa", - "sha256:96e3204aac292ee639edbfdef6298b4be2ea0a55a16b7068df91adac077cc606", - "sha256:a2bf639d0ce33b3ba38de777e08697b0d8f3dc07ab6802e4ac53fb65e3907af8", - "sha256:c95568ba1eda83812598c1dc9be60b4406dffd60b149bc1536180ad108723d6b" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==1.58.0" - }, - "pluggy": { - "hashes": [ - "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", - "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" - ], - "markers": "python_version >= '3.9'", - "version": "==1.6.0" - }, - "pyee": { - "hashes": [ - "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", - "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228" - ], - "markers": "python_version >= '3.8'", - "version": "==13.0.1" - }, - "pygments": { - "hashes": [ - "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", - "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" - ], - "markers": "python_version >= '3.8'", - "version": "==2.19.2" - }, - "pylint": { - "hashes": [ - "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", - "sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c" - ], - "index": "pypi", - "markers": "python_full_version >= '3.10.0'", - "version": "==4.0.5" - }, - "pytest": { - "hashes": [ - "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", - "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==8.4.2" - }, - "pytest-asyncio": { - "hashes": [ - "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", - "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==1.2.0" - }, - "pytest-base-url": { - "hashes": [ - "sha256:02748589a54f9e63fcbe62301d6b0496da0d10231b753e950c63e03aee745d45", - "sha256:3ad15611778764d451927b2a53240c1a7a591b521ea44cebfe45849d2d2812e6" - ], - "markers": "python_version >= '3.8'", - "version": "==2.1.0" - }, - "pytest-playwright": { - "hashes": [ - "sha256:247b61123b28c7e8febb993a187a07e54f14a9aa04edc166f7a976d88f04c770", - "sha256:8084e015b2b3ecff483c2160f1c8219b38b66c0d4578b23c0f700d1b0240ea38" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==0.7.2" - }, - "python-slugify": { - "hashes": [ - "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", - "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856" - ], - "markers": "python_version >= '3.7'", - "version": "==8.0.4" - }, - "requests": { - "hashes": [ - "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", - "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" - ], - "markers": "python_version >= '3.9'", - "version": "==2.32.5" - }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, - "tomlkit": { - "hashes": [ - "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", - "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064" - ], - "markers": "python_version >= '3.9'", - "version": "==0.14.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", - "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" - ], - "markers": "python_version >= '3.9'", - "version": "==4.15.0" - }, - "urllib3": { - "hashes": [ - "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", - "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4" - ], - "markers": "python_version >= '3.9'", - "version": "==2.6.3" - } - } -} diff --git a/README.md b/README.md index c7798a3e..6d0f1a49 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ docker run -p 5000:5000 -it \ falkordb/queryweaver ``` -> Note: To use OpenAI directly instead of Azure OpenAI, replace `AZURE_API_KEY` with `OPENAI_API_KEY` in the above command. +> Note: QueryWeaver supports multiple AI providers. You can use `OPENAI_API_KEY`, `GEMINI_API_KEY`, `ANTHROPIC_API_KEY`, or `AZURE_API_KEY`. See the [AI/LLM configuration](#aillm-configuration) section for details. > For a full list of configuration options, consult `.env.example`. @@ -231,6 +231,7 @@ with requests.post(url, headers=headers, json={"chat": ["Count orders last week" continue obj = json.loads(part) print('STREAM:', obj) +``` Notes & tips - Graph IDs are namespaced per-user. When calling the API directly use the plain graph id (the server will namespace by the authenticated user). For uploaded files the `database` field determines the saved graph id. @@ -245,7 +246,7 @@ Follow these steps to run and develop QueryWeaver from source. ### Prerequisites - Python 3.12+ -- pipenv +- uv (Python package manager) - A FalkorDB instance (local or remote) - Node.js and npm (for the React frontend) @@ -263,11 +264,11 @@ make install make run-dev ``` -If you prefer to set up manually or need a custom environment, use Pipenv: +If you prefer to set up manually or need a custom environment, use uv: ```bash # Install Python (backend) and frontend dependencies -pipenv sync --dev +uv sync # Create a local environment file cp .env.example .env @@ -277,7 +278,7 @@ cp .env.example .env ### Run the app locally ```bash -pipenv run uvicorn api.index:app --host 0.0.0.0 --port 5000 --reload +uv run uvicorn api.index:app --host 0.0.0.0 --port 5000 --reload ``` The server will be available at http://localhost:5000 @@ -326,49 +327,63 @@ APP_ENV=development ### AI/LLM configuration -QueryWeaver uses AI models for Text2SQL conversion and supports both Azure OpenAI and OpenAI directly. +QueryWeaver supports multiple AI providers. Set one API key and QueryWeaver auto-detects which provider to use. + +**Priority order:** Ollama > OpenAI > Gemini > Anthropic > Cohere > Azure (default) -#### Default: Azure OpenAI +| Provider | API Key | Default Models | +|----------|---------|----------------| +| Ollama | `OLLAMA_MODEL` | `ollama/`, `ollama/nomic-embed-text` | +| OpenAI | `OPENAI_API_KEY` | `openai/gpt-4.1`, `openai/text-embedding-ada-002` | +| Google Gemini | `GEMINI_API_KEY` | `gemini/gemini-3-pro-preview`, `gemini/gemini-embedding-001` | +| Anthropic | `ANTHROPIC_API_KEY` | `anthropic/claude-sonnet-4-5-20250929`, `voyage/voyage-3`* | +| Cohere | `COHERE_API_KEY` | `cohere/command-a-03-2025`, `cohere/embed-v4.0` | +| Azure OpenAI | `AZURE_API_KEY` | `azure/gpt-4.1`, `azure/text-embedding-ada-002` | -By default, QueryWeaver is configured to use Azure OpenAI. You need to set all three Azure credentials: +\* Anthropic has no native embeddings. You must set `VOYAGE_API_KEY` or `EMBEDDING_MODEL` for embeddings, otherwise startup will fail with an error. + +**Optional: Override default models** ```bash -AZURE_API_KEY=your_azure_api_key -AZURE_API_BASE=https://your-resource.openai.azure.com/ -AZURE_API_VERSION=2024-12-01-preview +COMPLETION_MODEL=gemini/gemini-3-pro-preview +EMBEDDING_MODEL=gemini/gemini-embedding-001 ``` -#### Alternative: OpenAI directly +Both must match your API key's provider. -To use OpenAI directly instead of Azure, simply set the `OPENAI_API_KEY` environment variable: +#### Docker examples with AI configuration +Using OpenAI: ```bash -OPENAI_API_KEY=your_openai_api_key +docker run -p 5000:5000 -it \ + -e FASTAPI_SECRET_KEY=your_secret_key \ + -e OPENAI_API_KEY=your_openai_api_key \ + falkordb/queryweaver ``` -When `OPENAI_API_KEY` is provided, QueryWeaver automatically switches to use OpenAI's models: -- Embedding model: `openai/text-embedding-ada-002` -- Completion model: `openai/gpt-4.1` - -This configuration is handled automatically in `api/config.py` - you only need to provide the appropriate API key. - -#### Docker examples with AI configuration +Using Google Gemini: +```bash +docker run -p 5000:5000 -it \ + -e FASTAPI_SECRET_KEY=your_secret_key \ + -e GEMINI_API_KEY=your_gemini_api_key \ + falkordb/queryweaver +``` -Using Azure OpenAI: +Using Anthropic: ```bash docker run -p 5000:5000 -it \ -e FASTAPI_SECRET_KEY=your_secret_key \ - -e AZURE_API_KEY=your_azure_api_key \ - -e AZURE_API_BASE=https://your-resource.openai.azure.com/ \ - -e AZURE_API_VERSION=2024-12-01-preview \ + -e ANTHROPIC_API_KEY=your_anthropic_api_key \ falkordb/queryweaver ``` -Using OpenAI directly: +Using Azure OpenAI: ```bash docker run -p 5000:5000 -it \ -e FASTAPI_SECRET_KEY=your_secret_key \ - -e OPENAI_API_KEY=your_openai_api_key \ + -e AZURE_API_KEY=your_azure_api_key \ + -e AZURE_API_BASE=https://your-resource.openai.azure.com/ \ + -e AZURE_API_VERSION=2024-12-01-preview \ falkordb/queryweaver ``` @@ -378,9 +393,9 @@ docker run -p 5000:5000 -it \ ### Prerequisites -- Install dev dependencies: `pipenv sync --dev` +- Install dev dependencies: `uv sync` - Start FalkorDB (see `make docker-falkordb`) -- Install Playwright browsers: `pipenv run playwright install` +- Install Playwright browsers: `uv run playwright install` ### Quick commands @@ -412,7 +427,7 @@ make test-e2e-headed ### Test types -- Unit tests: focus on individual modules and utilities. Run with `make test-unit` or `pipenv run pytest tests/ -k "not e2e"`. +- Unit tests: focus on individual modules and utilities. Run with `make test-unit` or `uv run python -m pytest tests/ -k "not e2e"`. - End-to-end (E2E) tests: run via Playwright and exercise UI flows, OAuth, file uploads, schema processing, chat queries, and API endpoints. Use `make test-e2e`. See `tests/e2e/README.md` for full E2E test instructions. @@ -424,7 +439,7 @@ GitHub Actions run unit and E2E tests on pushes and pull requests. Failures capt ## Troubleshooting - FalkorDB connection issues: start the DB helper `make docker-falkordb` or check network/host settings. -- Playwright/browser failures: install browsers with `pipenv run playwright install` and ensure system deps are present. +- Playwright/browser failures: install browsers with `uv run playwright install` and ensure system deps are present. - Missing environment variables: copy `.env.example` and fill required values. - **OAuth "mismatching_state: CSRF Warning!" errors**: Set `APP_ENV=production` (or `staging`) in your environment for HTTPS deployments, or `APP_ENV=development` for HTTP development environments. This ensures session cookies are configured correctly for your deployment type. diff --git a/api/agents/analysis_agent.py b/api/agents/analysis_agent.py index 2eeff49a..050f42c1 100644 --- a/api/agents/analysis_agent.py +++ b/api/agents/analysis_agent.py @@ -1,9 +1,7 @@ """Analysis agent for analyzing user queries and generating database analysis.""" from typing import List -from litellm import completion -from api.config import Config -from .utils import BaseAgent, parse_response +from .utils import BaseAgent, parse_response, run_completion class AnalysisAgent(BaseAgent): @@ -38,13 +36,10 @@ def get_analysis( # pylint: disable=too-many-arguments, too-many-positional-arg instructions, memory_context, database_type, user_rules_spec ) self.messages.append({"role": "user", "content": prompt}) - completion_result = completion( - model=Config.COMPLETION_MODEL, - messages=self.messages, - temperature=0, - ) - response = completion_result.choices[0].message.content + response = run_completion( + self.messages, self.custom_model, self.custom_api_key, temperature=0 + ) analysis = parse_response(response) if isinstance(analysis["ambiguities"], list): analysis["ambiguities"] = [ diff --git a/api/agents/follow_up_agent.py b/api/agents/follow_up_agent.py index 3798f3b5..b39661ec 100644 --- a/api/agents/follow_up_agent.py +++ b/api/agents/follow_up_agent.py @@ -1,8 +1,6 @@ """Follow-up agent for generating helpful questions when queries fail or are off-topic.""" -from litellm import completion -from api.config import Config -from .utils import BaseAgent +from .utils import BaseAgent, run_completion FOLLOW_UP_GENERATION_PROMPT = """ @@ -70,14 +68,11 @@ def generate_follow_up_question( ) try: - completion_result = completion( - model=Config.COMPLETION_MODEL, - messages=[{"role": "user", "content": prompt}], - temperature=0.9 + response = run_completion( + [{"role": "user", "content": prompt}], + self.custom_model, self.custom_api_key, temperature=0.9 ) - - response = completion_result.choices[0].message.content.strip() - return response + return response.strip() except Exception: # pylint: disable=broad-exception-caught # Fallback response if LLM call fails diff --git a/api/agents/relevancy_agent.py b/api/agents/relevancy_agent.py index 9317ec60..7db4f987 100644 --- a/api/agents/relevancy_agent.py +++ b/api/agents/relevancy_agent.py @@ -1,9 +1,7 @@ """Relevancy agent for determining relevancy of queries to database schema.""" import json -from litellm import completion -from api.config import Config -from .utils import BaseAgent, parse_response +from .utils import BaseAgent, parse_response, run_completion RELEVANCY_PROMPT = """ @@ -82,12 +80,9 @@ async def get_answer(self, user_question: str, database_desc: dict) -> dict: ), } ) - completion_result = completion( - model=Config.COMPLETION_MODEL, - messages=self.messages, - temperature=0, - ) - answer = completion_result.choices[0].message.content + answer = run_completion( + self.messages, self.custom_model, self.custom_api_key, temperature=0 + ) self.messages.append({"role": "assistant", "content": answer}) return parse_response(answer) diff --git a/api/agents/response_formatter_agent.py b/api/agents/response_formatter_agent.py index 198450b0..9e9dfe87 100644 --- a/api/agents/response_formatter_agent.py +++ b/api/agents/response_formatter_agent.py @@ -1,8 +1,7 @@ """Response formatter agent for generating user-readable responses from SQL query results.""" from typing import List, Dict -from litellm import completion -from api.config import Config +from .utils import run_completion RESPONSE_FORMATTER_PROMPT = """ @@ -43,8 +42,20 @@ class ResponseFormatterAgent: # pylint: disable=too-few-public-methods """Agent for generating user-readable responses from SQL query results.""" - def __init__(self): - """Initialize the response formatter agent.""" + def __init__(self, queries_history: List[str] = None, result_history: List[str] = None, + custom_api_key: str = None, custom_model: str = None): + """Initialize the response formatter agent. + + Args: + queries_history: List of previous user queries (for context) + result_history: List of previous results (for context) + custom_api_key: Optional custom API key for LLM calls + custom_model: Optional custom model name for LLM calls + """ + self.queries_history = queries_history or [] + self.result_history = result_history or [] + self.custom_api_key = custom_api_key + self.custom_model = custom_model def format_response(self, user_query: str, sql_query: str, query_results: List[Dict], db_description: str = "") -> str: @@ -64,14 +75,10 @@ def format_response(self, user_query: str, sql_query: str, messages = [{"role": "user", "content": prompt}] - completion_result = completion( - model=Config.COMPLETION_MODEL, - messages=messages, - temperature=0.3, # Slightly higher temperature for more natural responses - top_p=1, + response = run_completion( + messages, self.custom_model, self.custom_api_key, + temperature=0.3 # Slightly higher temperature for more natural responses ) - - response = completion_result.choices[0].message.content return response.strip() def _build_response_prompt(self, user_query: str, sql_query: str, diff --git a/api/agents/utils.py b/api/agents/utils.py index ceafff23..bc28c99f 100644 --- a/api/agents/utils.py +++ b/api/agents/utils.py @@ -1,13 +1,37 @@ """Utility functions for agents.""" import json -from typing import Any, Dict +from typing import Any, Dict, List + +from litellm import completion +from api.config import Config + + +def run_completion(messages: List[Dict[str, str]], custom_model: str = None, + custom_api_key: str = None, **kwargs) -> str: + """Run an LLM completion with optional custom model/key overrides. + + Returns the content string from the first choice. + """ + completion_args = { + "model": custom_model if custom_model else Config.COMPLETION_MODEL, + "messages": messages, + "top_p": 1, + **kwargs, + } + + if custom_api_key: + completion_args["api_key"] = custom_api_key + + result = completion(**completion_args) + return result.choices[0].message.content class BaseAgent: # pylint: disable=too-few-public-methods """Base class for agents.""" - def __init__(self, queries_history: list, result_history: list): + def __init__(self, queries_history: list, result_history: list, + custom_api_key: str = None, custom_model: str = None): """Initialize the agent with query and result history.""" if result_history is None: self.messages = [] @@ -17,6 +41,9 @@ def __init__(self, queries_history: list, result_history: list): self.messages.append({"role": "user", "content": query}) self.messages.append({"role": "assistant", "content": result}) + self.custom_api_key = custom_api_key + self.custom_model = custom_model + def parse_response(response: str) -> Dict[str, Any]: """ diff --git a/api/app_factory.py b/api/app_factory.py index 7c93cce9..3b95b5bf 100644 --- a/api/app_factory.py +++ b/api/app_factory.py @@ -5,7 +5,6 @@ import os import secrets -from dotenv import load_dotenv from fastapi import FastAPI, Request, HTTPException from fastapi.responses import RedirectResponse, JSONResponse, FileResponse from fastapi.staticfiles import StaticFiles @@ -22,9 +21,8 @@ from api.routes.graphs import graphs_router from api.routes.database import database_router from api.routes.tokens import tokens_router +from api.routes.settings import settings_router -# Load environment variables from .env file -load_dotenv() logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) @@ -60,14 +58,17 @@ def _is_secure_request(request: Request) -> bool: """Determine if the request is over HTTPS.""" forwarded_proto = request.headers.get("x-forwarded-proto") if forwarded_proto: - return forwarded_proto == "https" + # Normalize: proxies may send comma-separated or mixed-case values + first_proto = forwarded_proto.split(",")[0].strip().lower() + return first_proto == "https" return request.url.scheme == "https" class CSRFMiddleware(BaseHTTPMiddleware): # pylint: disable=too-few-public-methods """Double Submit Cookie CSRF protection. - Sets a csrf_token cookie (readable by JS) on every response. + Ensures a csrf_token cookie (readable by JS) exists, setting it + on the response if the incoming request does not already carry one. State-changing requests must echo the cookie value back via the X-CSRF-Token header. Bearer-token authenticated requests and auth/login endpoints are exempt. @@ -129,10 +130,10 @@ def _ensure_csrf_cookie(self, request: Request, response): ) -def create_app(): +def create_app(): # pylint: disable=too-many-statements """Create and configure the FastAPI application.""" - # Create the FastAPI app instance just to set the o routes + # Create the FastAPI app instance with original routes # Will be merged with MCP app later if MCP is enabled app = FastAPI( title="QueryWeaver" @@ -143,6 +144,7 @@ def create_app(): app.include_router(graphs_router, prefix="/graphs") app.include_router(database_router) app.include_router(tokens_router, prefix="/tokens") + app.include_router(settings_router, prefix="/settings") diff --git a/api/auth/user_management.py b/api/auth/user_management.py index c7a82196..3b5cd00f 100644 --- a/api/auth/user_management.py +++ b/api/auth/user_management.py @@ -235,11 +235,13 @@ async def validate_user(request: Request) -> Tuple[Optional[Dict[str, Any]], boo try: api_token = get_token(request) - if api_token: - db_info = await _get_user_info(api_token) + if not api_token: + return None, False - if db_info: - return db_info, True + db_info = await _get_user_info(api_token) + + if db_info: + return db_info, True return None, False diff --git a/api/config.py b/api/config.py index 7063f067..b029970c 100644 --- a/api/config.py +++ b/api/config.py @@ -7,8 +7,13 @@ import logging import dataclasses from typing import Union + +from dotenv import load_dotenv from litellm import embedding +# Ensure .env is loaded before Config reads os.getenv() at class definition time +load_dotenv() + # Configure litellm logging to prevent sensitive data leakage def configure_litellm_logging(): """Configure litellm to suppress completion logs.""" @@ -58,19 +63,70 @@ def get_vector_size(self) -> int: return size +def _with_prefix(model: str, provider: str) -> str: + """Ensure a model string has exactly one provider prefix.""" + prefix = f"{provider}/" + return prefix + model.removeprefix(prefix) + + +SUPPORTED_VENDORS = ("openai", "anthropic", "gemini", "azure", "ollama", "cohere") + + @dataclasses.dataclass class Config: """ Configuration class for the text2sql module. """ - AZURE_FLAG = True - if not os.getenv("OPENAI_API_KEY"): - EMBEDDING_MODEL_NAME = "azure/text-embedding-ada-002" - COMPLETION_MODEL = "azure/gpt-4.1" - else: + + # User-provided overrides via env vars + _user_completion = os.getenv("COMPLETION_MODEL", "") + _user_embedding = os.getenv("EMBEDDING_MODEL", "") + + # Determine the provider and models based on available API keys + # Priority: Ollama > OpenAI > Gemini > Anthropic > Cohere > Azure (default) + if os.getenv("OLLAMA_MODEL"): + LLM_PROVIDER = "ollama" + AZURE_FLAG = False + COMPLETION_MODEL = _user_completion or _with_prefix( + os.getenv("OLLAMA_MODEL"), "ollama") + EMBEDDING_MODEL_NAME = _user_embedding or _with_prefix( + os.getenv("OLLAMA_EMBEDDING_MODEL", "nomic-embed-text"), "ollama") + elif os.getenv("OPENAI_API_KEY"): + LLM_PROVIDER = "openai" + AZURE_FLAG = False + COMPLETION_MODEL = _user_completion or "openai/gpt-4.1" + EMBEDDING_MODEL_NAME = _user_embedding or "openai/text-embedding-ada-002" + elif os.getenv("GEMINI_API_KEY"): + LLM_PROVIDER = "gemini" AZURE_FLAG = False - EMBEDDING_MODEL_NAME = "openai/text-embedding-ada-002" - COMPLETION_MODEL = "openai/gpt-4.1" + COMPLETION_MODEL = _user_completion or "gemini/gemini-3-pro-preview" + EMBEDDING_MODEL_NAME = _user_embedding or "gemini/gemini-embedding-001" + elif os.getenv("ANTHROPIC_API_KEY"): + LLM_PROVIDER = "anthropic" + AZURE_FLAG = False + COMPLETION_MODEL = _user_completion or "anthropic/claude-sonnet-4-5-20250929" + if _user_embedding: + EMBEDDING_MODEL_NAME = _user_embedding + elif os.getenv("VOYAGE_API_KEY"): + EMBEDDING_MODEL_NAME = "voyage/voyage-3" + else: + raise ValueError( + "ANTHROPIC_API_KEY is set, but Anthropic has no native embeddings. " + "Set EMBEDDING_MODEL or VOYAGE_API_KEY for embeddings." + ) + elif os.getenv("COHERE_API_KEY"): + LLM_PROVIDER = "cohere" + AZURE_FLAG = False + COMPLETION_MODEL = _user_completion or _with_prefix( + os.getenv("COHERE_MODEL", "command-a-03-2025"), "cohere") + EMBEDDING_MODEL_NAME = _user_embedding or _with_prefix( + os.getenv("COHERE_EMBEDDING_MODEL", "embed-v4.0"), "cohere") + else: + # Default to Azure + LLM_PROVIDER = "azure" + AZURE_FLAG = True + COMPLETION_MODEL = _user_completion or "azure/gpt-4.1" + EMBEDDING_MODEL_NAME = _user_embedding or "azure/text-embedding-ada-002" DB_MAX_DISTINCT: int = 100 # pylint: disable=invalid-name DB_UNIQUENESS_THRESHOLD: float = 0.5 # pylint: disable=invalid-name diff --git a/api/core/schema_loader.py b/api/core/schema_loader.py index bb4dcedb..b1568514 100644 --- a/api/core/schema_loader.py +++ b/api/core/schema_loader.py @@ -13,6 +13,7 @@ from api.loaders.base_loader import BaseLoader from api.loaders.postgres_loader import PostgresLoader from api.loaders.mysql_loader import MySQLLoader +from api.loaders.snowflake_loader import SnowflakeLoader # Use the same delimiter as in the JavaScript frontend for streaming chunks MESSAGE_DELIMITER = "|||FALKORDB_MESSAGE_BOUNDARY|||" @@ -44,6 +45,9 @@ def _step_detect_db_type(steps_counter: int, url: str) -> tuple[type[BaseLoader] elif url.startswith("mysql://"): db_type = "mysql" loader = MySQLLoader + elif url.startswith("snowflake://"): + db_type = "snowflake" + loader = SnowflakeLoader else: raise InvalidArgumentError("Invalid database URL format") diff --git a/api/core/text2sql.py b/api/core/text2sql.py index efdca397..65bc0b4f 100644 --- a/api/core/text2sql.py +++ b/api/core/text2sql.py @@ -15,10 +15,12 @@ from api.agents import AnalysisAgent, RelevancyAgent, ResponseFormatterAgent, FollowUpAgent from api.agents.healer_agent import HealerAgent from api.config import Config +from api.config import SUPPORTED_VENDORS from api.extensions import db from api.graph import find, get_db_description, get_user_rules from api.loaders.postgres_loader import PostgresLoader from api.loaders.mysql_loader import MySQLLoader +from api.loaders.snowflake_loader import SnowflakeLoader from api.memory.graphiti_tool import MemoryTool from api.sql_utils import SQLIdentifierQuoter, DatabaseSpecificQuoter @@ -45,6 +47,8 @@ class ChatRequest(BaseModel): chat: list[str] result: list[str] | None = None instructions: str | None = None + custom_api_key: str | None = None + custom_model: str | None = None use_user_rules: bool = True # If True, fetch rules from database; if False, don't use rules use_memory: bool = True @@ -58,6 +62,8 @@ class ConfirmRequest(BaseModel): sql_query: str confirmation: str = "" chat: list = [] + custom_api_key: str | None = None + custom_model: str | None = None def get_database_type_and_loader(db_url: str): @@ -79,6 +85,8 @@ def get_database_type_and_loader(db_url: str): return 'postgresql', PostgresLoader if db_url_lower.startswith('mysql://'): return 'mysql', MySQLLoader + if db_url_lower.startswith('snowflake://'): + return 'snowflake', SnowflakeLoader # Default to PostgresLoader for backward compatibility return 'postgresql', PostgresLoader @@ -248,9 +256,25 @@ async def generate(): # pylint: disable=too-many-locals,too-many-branches,too-m logging.info("Starting query processing pipeline for query: %s", sanitize_query(queries_history[-1])) # nosemgrep - agent_rel = RelevancyAgent(queries_history, result_history) - agent_an = AnalysisAgent(queries_history, result_history) - follow_up_agent = FollowUpAgent(queries_history, result_history) + # Extract custom API key and model from chat_data + custom_api_key = chat_data.custom_api_key + custom_model = chat_data.custom_model + + # Validate custom model format (vendor/model) + if custom_model: + parts = custom_model.split("/", 1) + if len(parts) != 2 or not parts[0] or not parts[1]: + raise InvalidArgumentError( + "Invalid model format. Expected 'vendor/model' (e.g. 'openai/gpt-4.1')" + ) + if parts[0] not in SUPPORTED_VENDORS: + raise InvalidArgumentError( + f"Unsupported vendor '{parts[0]}'. Supported: {', '.join(SUPPORTED_VENDORS)}" + ) + + agent_rel = RelevancyAgent(queries_history, result_history, custom_api_key, custom_model) + agent_an = AnalysisAgent(queries_history, result_history, custom_api_key, custom_model) + follow_up_agent = FollowUpAgent(queries_history, result_history, custom_api_key, custom_model) step = {"type": "reasoning_step", "final_response": False, @@ -576,7 +600,9 @@ def execute_sql(sql: str): "message": f"Step {step_num}: Generating user-friendly response"} yield json.dumps(step) + MESSAGE_DELIMITER - response_agent = ResponseFormatterAgent() + response_agent = ResponseFormatterAgent( + queries_history, result_history, custom_api_key, custom_model + ) user_readable_response = response_agent.format_response( user_query=queries_history[-1], sql_query=answer_an["sql_query"], @@ -715,6 +741,8 @@ async def execute_destructive_operation( # pylint: disable=too-many-statements sql_query = confirm_data.sql_query if hasattr(confirm_data, 'sql_query') else "" queries_history = confirm_data.chat if hasattr(confirm_data, 'chat') else [] + custom_api_key = confirm_data.custom_api_key + custom_model = confirm_data.custom_model if not sql_query: raise InvalidArgumentError("No SQL query provided") @@ -723,6 +751,7 @@ async def execute_destructive_operation( # pylint: disable=too-many-statements async def generate_confirmation(): # pylint: disable=too-many-locals,too-many-statements # Create memory tool for saving query results memory_tool = await MemoryTool.create(user_id, graph_id) + result_history = [] # Initialize result_history for this context if confirmation == "CONFIRM": try: @@ -823,7 +852,9 @@ async def generate_confirmation(): # pylint: disable=too-many-locals,too-many-s "message": f"Step {step_num}: Generating user-friendly response"} yield json.dumps(step) + MESSAGE_DELIMITER - response_agent = ResponseFormatterAgent() + response_agent = ResponseFormatterAgent( + queries_history, result_history, custom_api_key, custom_model + ) user_readable_response = response_agent.format_response( user_query=queries_history[-1] if queries_history else "Destructive operation", sql_query=sql_query, diff --git a/api/index.py b/api/index.py index 829e3e0a..1bb7d061 100644 --- a/api/index.py +++ b/api/index.py @@ -1,6 +1,10 @@ """Main entry point for the text2sql API.""" -from api.app_factory import create_app +# Load .env before any app imports that read os.getenv at module level +from dotenv import load_dotenv +load_dotenv() + +from api.app_factory import create_app # pylint: disable=wrong-import-position app = create_app() diff --git a/api/loaders/postgres_loader.py b/api/loaders/postgres_loader.py index be0be497..9d58f4e2 100644 --- a/api/loaders/postgres_loader.py +++ b/api/loaders/postgres_loader.py @@ -5,6 +5,7 @@ import decimal import logging from typing import AsyncGenerator, Dict, Any, List, Tuple +from urllib.parse import urlparse, parse_qs, unquote import psycopg2 from psycopg2 import sql @@ -52,7 +53,7 @@ class PostgresLoader(BaseLoader): @staticmethod def _execute_sample_query( - cursor, table_name: str, col_name: str, sample_size: int = 3 + cursor: Any, table_name: str, col_name: str, sample_size: int = 3 ) -> List[Any]: """ Execute query to get random sample values for a column. @@ -96,6 +97,48 @@ def _serialize_value(value): return None return value + @staticmethod + def parse_schema_from_url(connection_url: str) -> str: + """ + Parse the search_path from the connection URL's options parameter. + + The options parameter follows PostgreSQL's libpq format: + postgresql://user:pass@host:port/db?options=-csearch_path%3Dschema_name + + Args: + connection_url: PostgreSQL connection URL + + Returns: + The first schema from search_path, or 'public' if not specified + """ + try: + parsed = urlparse(connection_url) + query_params = parse_qs(parsed.query) + + options = query_params.get('options', []) + if not options: + return 'public' + + options_str = unquote(options[0]) + + # Parse -c search_path=value from options + # Format can be: -csearch_path=schema or -c search_path=schema + # Match comma-separated schema tokens (supports spaces after commas). + match = re.search(r'-c\s*search_path\s*=\s*([^\s,]+(?:\s*,\s*[^\s,]+)*)', options_str, re.IGNORECASE) + if match: + search_path = match.group(1) + schemas = search_path.split(',') + for s in schemas: + s = s.strip().strip('"\'') + if s and s != '$user': + return s + return 'public' + + return 'public' + + except Exception: # pylint: disable=broad-exception-caught + return 'public' + @staticmethod async def load(prefix: str, connection_url: str) -> AsyncGenerator[tuple[bool, str], None]: """ @@ -103,16 +146,29 @@ async def load(prefix: str, connection_url: str) -> AsyncGenerator[tuple[bool, s Args: connection_url: PostgreSQL connection URL in format: - postgresql://username:password@host:port/database + postgresql://username:password@host:port/database + Optionally with schema via options parameter: + postgresql://...?options=-csearch_path%3Dschema_name Returns: Tuple[bool, str]: Success status and message """ + conn = None + cursor = None try: + # Parse schema from connection URL (defaults to 'public') + schema = PostgresLoader.parse_schema_from_url(connection_url) + # Connect to PostgreSQL database conn = psycopg2.connect(connection_url) cursor = conn.cursor() + # Set the session search_path to the parsed schema so unqualified + # table references (e.g. in sample queries) resolve correctly. + cursor.execute( + sql.SQL("SET search_path TO {}").format(sql.Identifier(schema)) + ) + # Extract database name from connection URL db_name = connection_url.split('/')[-1] if '?' in db_name: @@ -120,15 +176,17 @@ async def load(prefix: str, connection_url: str) -> AsyncGenerator[tuple[bool, s # Get all table information yield True, "Extracting table information..." - entities = PostgresLoader.extract_tables_info(cursor) + entities = PostgresLoader.extract_tables_info(cursor, schema) yield True, "Extracting relationship information..." # Get all relationship information - relationships = PostgresLoader.extract_relationships(cursor) + relationships = PostgresLoader.extract_relationships(cursor, schema) - # Close database connection + # Close database connection before graph loading cursor.close() + cursor = None conn.close() + conn = None yield True, "Loading data into graph..." # Load data into graph @@ -144,21 +202,27 @@ async def load(prefix: str, connection_url: str) -> AsyncGenerator[tuple[bool, s except Exception as e: # pylint: disable=broad-exception-caught logging.error("Error loading PostgreSQL schema: %s", e) yield False, "Failed to load PostgreSQL database schema" + finally: + if cursor is not None: + cursor.close() + if conn is not None: + conn.close() @staticmethod - def extract_tables_info(cursor) -> Dict[str, Any]: + def extract_tables_info(cursor: Any, schema: str = 'public') -> Dict[str, Any]: """ Extract table and column information from PostgreSQL database. Args: cursor: Database cursor + schema: Database schema to extract tables from (default: 'public') Returns: Dict containing table information """ entities = {} - # Get all tables in public schema + # Get all tables in the specified schema cursor.execute(""" SELECT table_name, table_comment FROM information_schema.tables t @@ -166,13 +230,14 @@ def extract_tables_info(cursor) -> Dict[str, Any]: SELECT schemaname, tablename, description as table_comment FROM pg_tables pt JOIN pg_class pc ON pc.relname = pt.tablename + JOIN pg_namespace pn ON pn.oid = pc.relnamespace AND pn.nspname = pt.schemaname JOIN pg_description pd ON pd.objoid = pc.oid AND pd.objsubid = 0 - WHERE pt.schemaname = 'public' + WHERE pt.schemaname = %s ) tc ON tc.tablename = t.table_name - WHERE t.table_schema = 'public' + WHERE t.table_schema = %s AND t.table_type = 'BASE TABLE' ORDER BY t.table_name; - """) + """, (schema, schema)) tables = cursor.fetchall() @@ -180,10 +245,10 @@ def extract_tables_info(cursor) -> Dict[str, Any]: table_name = table_name.strip() # Get column information for this table - columns_info = PostgresLoader.extract_columns_info(cursor, table_name) + columns_info = PostgresLoader.extract_columns_info(cursor, table_name, schema) # Get foreign keys for this table - foreign_keys = PostgresLoader.extract_foreign_keys(cursor, table_name) + foreign_keys = PostgresLoader.extract_foreign_keys(cursor, table_name, schema) # Generate table description table_description = table_comment if table_comment else f"Table: {table_name}" @@ -201,13 +266,14 @@ def extract_tables_info(cursor) -> Dict[str, Any]: return entities @staticmethod - def extract_columns_info(cursor, table_name: str) -> Dict[str, Any]: + def extract_columns_info(cursor: Any, table_name: str, schema: str = 'public') -> Dict[str, Any]: """ Extract column information for a specific table. Args: cursor: Database cursor table_name: Name of the table + schema: Database schema (default: 'public') Returns: Dict containing column information @@ -230,7 +296,9 @@ def extract_columns_info(cursor, table_name: str) -> Dict[str, Any]: FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage ku ON tc.constraint_name = ku.constraint_name + AND tc.constraint_schema = ku.constraint_schema WHERE tc.table_name = %s + AND tc.table_schema = %s AND tc.constraint_type = 'PRIMARY KEY' ) pk ON pk.column_name = c.column_name LEFT JOIN ( @@ -238,16 +306,19 @@ def extract_columns_info(cursor, table_name: str) -> Dict[str, Any]: FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage ku ON tc.constraint_name = ku.constraint_name + AND tc.constraint_schema = ku.constraint_schema WHERE tc.table_name = %s + AND tc.table_schema = %s AND tc.constraint_type = 'FOREIGN KEY' ) fk ON fk.column_name = c.column_name - LEFT JOIN pg_class pc ON pc.relname = c.table_name + LEFT JOIN pg_namespace pn ON pn.nspname = c.table_schema + LEFT JOIN pg_class pc ON pc.relname = c.table_name AND pc.relnamespace = pn.oid LEFT JOIN pg_attribute pa ON pa.attrelid = pc.oid AND pa.attname = c.column_name LEFT JOIN pg_description pgd ON pgd.objoid = pc.oid AND pgd.objsubid = pa.attnum WHERE c.table_name = %s - AND c.table_schema = 'public' + AND c.table_schema = %s ORDER BY c.ordinal_position; - """, (table_name, table_name, table_name)) + """, (table_name, schema, table_name, schema, table_name, schema)) columns = cursor.fetchall() columns_info = {} @@ -289,13 +360,14 @@ def extract_columns_info(cursor, table_name: str) -> Dict[str, Any]: return columns_info @staticmethod - def extract_foreign_keys(cursor, table_name: str) -> List[Dict[str, str]]: + def extract_foreign_keys(cursor: Any, table_name: str, schema: str = 'public') -> List[Dict[str, str]]: """ Extract foreign key information for a specific table. Args: cursor: Database cursor table_name: Name of the table + schema: Database schema (default: 'public') Returns: List of foreign key dictionaries @@ -315,8 +387,8 @@ def extract_foreign_keys(cursor, table_name: str) -> List[Dict[str, str]]: AND ccu.table_schema = tc.table_schema WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = %s - AND tc.table_schema = 'public'; - """, (table_name,)) + AND tc.table_schema = %s; + """, (table_name, schema)) foreign_keys = [] for constraint_name, column_name, foreign_table, foreign_column in cursor.fetchall(): @@ -330,12 +402,13 @@ def extract_foreign_keys(cursor, table_name: str) -> List[Dict[str, str]]: return foreign_keys @staticmethod - def extract_relationships(cursor) -> Dict[str, List[Dict[str, str]]]: + def extract_relationships(cursor: Any, schema: str = 'public') -> Dict[str, List[Dict[str, str]]]: """ Extract all relationship information from the database. Args: cursor: Database cursor + schema: Database schema (default: 'public') Returns: Dict containing relationship information @@ -355,9 +428,9 @@ def extract_relationships(cursor) -> Dict[str, List[Dict[str, str]]]: ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema WHERE tc.constraint_type = 'FOREIGN KEY' - AND tc.table_schema = 'public' + AND tc.table_schema = %s ORDER BY tc.table_name, tc.constraint_name; - """) + """, (schema,)) relationships = {} for (table_name, constraint_name, column_name, diff --git a/api/loaders/snowflake_loader.py b/api/loaders/snowflake_loader.py new file mode 100644 index 00000000..7685daa9 --- /dev/null +++ b/api/loaders/snowflake_loader.py @@ -0,0 +1,711 @@ +"""Snowflake loader for loading database schemas into FalkorDB graphs.""" + +import base64 +import datetime +import decimal +import logging +import re +from typing import AsyncGenerator, Dict, Any, List, Tuple +from urllib.parse import urlparse, parse_qs + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization + +import tqdm +import snowflake.connector +from snowflake.connector import DictCursor + +from api.loaders.base_loader import BaseLoader +from api.loaders.graph_loader import load_to_graph + + +class SnowflakeQueryError(Exception): + """Exception raised for Snowflake query execution errors.""" + + +class SnowflakeConnectionError(Exception): + """Exception raised for Snowflake connection errors.""" + + +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") + + +class SnowflakeLoader(BaseLoader): + """ + Loader for Snowflake databases that connects and extracts schema information. + """ + + # DDL operations that modify database schema # pylint: disable=duplicate-code + SCHEMA_MODIFYING_OPERATIONS = { + 'CREATE', 'ALTER', 'DROP', 'RENAME', 'TRUNCATE' + } + + # More specific patterns for schema-affecting operations + SCHEMA_PATTERNS = [ # pylint: disable=duplicate-code + r'^\s*CREATE\s+TABLE', + r'^\s*CREATE\s+INDEX', + r'^\s*CREATE\s+UNIQUE\s+INDEX', + r'^\s*ALTER\s+TABLE', + r'^\s*DROP\s+TABLE', + r'^\s*DROP\s+INDEX', + r'^\s*RENAME\s+TABLE', + r'^\s*TRUNCATE\s+TABLE', + r'^\s*CREATE\s+VIEW', + r'^\s*DROP\s+VIEW', + r'^\s*CREATE\s+DATABASE', + r'^\s*DROP\s+DATABASE', + r'^\s*CREATE\s+SCHEMA', + r'^\s*DROP\s+SCHEMA', + ] + + @staticmethod + def _validate_identifier(identifier: str, identifier_type: str = "identifier") -> None: + """ + Validate that an identifier (table, column, database, schema name) is safe. + + Args: + identifier: The identifier to validate + identifier_type: Type of identifier for error messages + + Raises: + ValueError: If identifier contains invalid characters + """ + # Allow alphanumeric, underscore, dollar sign, and limit to reasonable length + # Snowflake identifiers can contain these characters when quoted + if not re.match(r'^[A-Za-z0-9_$]+$', identifier): + raise ValueError( + f"Invalid {identifier_type}: {identifier!r}. " + "Only alphanumeric characters, underscore, and dollar sign are allowed." + ) + if len(identifier) > 255: + raise ValueError(f"Invalid {identifier_type}: exceeds maximum length of 255") + + @staticmethod + def _quote_identifier(identifier: str) -> str: + """ + Safely quote a Snowflake identifier by escaping double quotes. + + Args: + identifier: The identifier to quote + + Returns: + Quoted identifier safe for SQL interpolation + """ + # Escape any existing double quotes by doubling them + escaped = identifier.replace('"', '""') + return f'"{escaped}"' + + @staticmethod + def _execute_sample_query( + cursor, table_name: str, col_name: str, sample_size: int = 3 + ) -> List[Any]: + """ + Execute query to get random sample values for a column. + Snowflake implementation using SAMPLE for random sampling. + """ + # Validate identifiers to prevent SQL injection + SnowflakeLoader._validate_identifier(table_name, "table name") + SnowflakeLoader._validate_identifier(col_name, "column name") + + # Validate sample_size is a positive integer + if not isinstance(sample_size, int) or sample_size <= 0: + raise ValueError(f"sample_size must be a positive integer, got {sample_size!r}") + + # Quote identifiers safely + quoted_table = SnowflakeLoader._quote_identifier(table_name) + quoted_col = SnowflakeLoader._quote_identifier(col_name) + + # Oversample by 10x to increase the chance of getting sample_size + # distinct non-null values after filtering (Snowflake's SAMPLE clause + # returns approximate row counts, and rows may contain NULLs or duplicates) + sample_rows = sample_size * 10 + + query = f""" + SELECT DISTINCT {quoted_col} + FROM {quoted_table} SAMPLE ({sample_rows} ROWS) + WHERE {quoted_col} IS NOT NULL + LIMIT %s; + """ + cursor.execute(query, (sample_size,)) + + sample_results = cursor.fetchall() + # DictCursor returns dicts; extract the column value by name + return [row[col_name] for row in sample_results if row[col_name] is not None] + + @staticmethod + def _serialize_value(value): + """ + Convert non-JSON serializable values to JSON serializable format. + + Args: + value: The value to serialize + + Returns: + JSON serializable version of the value + """ + if isinstance(value, (datetime.date, datetime.datetime)): + return value.isoformat() + if isinstance(value, datetime.time): + return value.isoformat() + if isinstance(value, decimal.Decimal): + return float(value) + if value is None: + return None + return value + + @staticmethod + def _parse_snowflake_url(connection_url: str) -> Dict[str, Any]: # pylint: disable=too-many-locals + """ + Parse Snowflake connection URL into components. + + Supports two authentication modes: + - Password: snowflake://user:pass@account/db/schema?warehouse=WH + - Key-pair: snowflake://user@account/db/schema?warehouse=WH&private_key=BASE64_PEM + (optionally with &private_key_passphrase=PASSPHRASE) + + Args: + connection_url: Snowflake connection URL + + Returns: + Dict with connection parameters for snowflake.connector.connect() + """ + if not connection_url.startswith('snowflake://'): + raise ValueError( + "Invalid Snowflake URL format. Expected " + "snowflake://username:password@account/database/schema?warehouse=warehouse_name" + ) + + parsed = urlparse(connection_url) + + if not parsed.username: + raise ValueError("Snowflake URL must include username") + + username = parsed.username + password = parsed.password or "" + + if not parsed.hostname: + raise ValueError("Snowflake URL must include account") + account = parsed.hostname + + path_parts = [p for p in parsed.path.split('/') if p] + if len(path_parts) < 1: + raise ValueError("Snowflake URL must include database name") + + database = path_parts[0] + schema = path_parts[1] if len(path_parts) > 1 else "PUBLIC" + + query_params = parse_qs(parsed.query) + warehouse = query_params.get('warehouse', ['COMPUTE_WH'])[0] + + # Validate all identifiers + SnowflakeLoader._validate_identifier(database, "database") + SnowflakeLoader._validate_identifier(schema, "schema") + SnowflakeLoader._validate_identifier(warehouse, "warehouse") + + conn_params: Dict[str, Any] = { + 'user': username, + 'account': account, + 'database': database, + 'schema': schema, + 'warehouse': warehouse, + 'login_timeout': 30, + 'network_timeout': 60, + } + + # Check for key-pair authentication + private_key_b64 = query_params.get('private_key', [None])[0] + if private_key_b64: + passphrase = query_params.get('private_key_passphrase', [None])[0] + passphrase_bytes = passphrase.encode() if passphrase else None + + try: + # Handle both standard and URL-safe base64 (browsers may + # convert '+' to spaces when URL-encoding query params) + cleaned_b64 = private_key_b64.replace(' ', '+') + pem_bytes = base64.b64decode(cleaned_b64) + private_key = serialization.load_pem_private_key( + pem_bytes, + password=passphrase_bytes, + backend=default_backend(), + ) + conn_params['private_key'] = private_key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + except Exception as e: + raise ValueError(f"Failed to load private key: {e}") from e + else: + conn_params['password'] = password + + return conn_params + + @staticmethod + async def load(prefix: str, connection_url: str) -> AsyncGenerator[ + tuple[bool, str], None + ]: + """ + Load the graph data from a Snowflake database into the graph database. + + Args: + connection_url: Snowflake connection URL in format: + snowflake://username:password@account/database/schema?warehouse=warehouse_name + + Returns: + Tuple[bool, str]: Success status and message + """ + try: + # Parse connection URL + conn_params = SnowflakeLoader._parse_snowflake_url(connection_url) + + # Connect to Snowflake database + conn = snowflake.connector.connect(**conn_params) + cursor = conn.cursor(DictCursor) + + # Get database and schema name + db_name = conn_params['database'] + # Snowflake stores unquoted identifiers in UPPERCASE; + # INFORMATION_SCHEMA lookups require the canonical form. + schema_name = conn_params['schema'].upper() + + # Get all table information + yield True, "Extracting table information..." + entities = SnowflakeLoader.extract_tables_info(cursor, db_name, schema_name) + + # Get all relationship information + yield True, "Extracting relationship information..." + relationships = SnowflakeLoader.extract_relationships(cursor, db_name, schema_name) + + # Close database connection + cursor.close() + conn.close() + + # Load data into graph + yield True, "Loading data into graph..." + await load_to_graph(f"{prefix}_{db_name}", entities, relationships, + db_name=db_name, db_url=connection_url) + + yield True, (f"Snowflake schema loaded successfully. " + f"Found {len(entities)} tables.") + + except snowflake.connector.Error as e: + logging.error("Snowflake error: %s", e) + yield False, f"Snowflake error: {e}" + except Exception as e: # pylint: disable=broad-exception-caught + logging.error("Error loading Snowflake schema: %s", e) + yield False, f"Failed to load Snowflake database schema: {e}" + + @staticmethod + def extract_tables_info(cursor, db_name: str, schema_name: str) -> Dict[str, Any]: + """ + Extract table and column information from Snowflake database. + + Args: + cursor: Database cursor + db_name: Database name + schema_name: Schema name + + Returns: + Dict containing table information + """ + # Validate identifiers to prevent SQL injection + SnowflakeLoader._validate_identifier(db_name, "database name") + SnowflakeLoader._validate_identifier(schema_name, "schema name") + + entities = {} + + # Get all tables in the schema + # Use quoted identifiers for database name, parameterize schema_name + quoted_db = SnowflakeLoader._quote_identifier(db_name) + cursor.execute(f""" + SELECT TABLE_NAME, COMMENT + FROM {quoted_db}.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = %s + AND TABLE_TYPE = 'BASE TABLE' + ORDER BY TABLE_NAME; + """, (schema_name,)) + + tables = cursor.fetchall() + + for table_info in tqdm.tqdm(tables, desc="Extracting table information"): + table_name = table_info['TABLE_NAME'] + table_comment = table_info['COMMENT'] + + # Get column information for this table + columns_info = SnowflakeLoader.extract_columns_info( + cursor, db_name, schema_name, table_name + ) + + # Get foreign keys for this table + foreign_keys = SnowflakeLoader.extract_foreign_keys( + cursor, db_name, schema_name, table_name + ) + + # Generate table description + table_description = table_comment if table_comment else f"Table: {table_name}" + + # Get column descriptions for batch embedding + col_descriptions = [col_info['description'] for col_info in columns_info.values()] + + entities[table_name] = { + 'description': table_description, + 'columns': columns_info, + 'foreign_keys': foreign_keys, + 'col_descriptions': col_descriptions + } + + return entities + + @staticmethod + def extract_columns_info( # pylint: disable=too-many-locals + cursor, db_name: str, schema_name: str, table_name: str + ) -> Dict[str, Any]: + """ + Extract column information for a specific table. + + Args: + cursor: Database cursor + db_name: Database name + schema_name: Schema name + table_name: Name of the table + + Returns: + Dict containing column information + """ + # Validate identifiers to prevent SQL injection + SnowflakeLoader._validate_identifier(db_name, "database name") + SnowflakeLoader._validate_identifier(schema_name, "schema name") + SnowflakeLoader._validate_identifier(table_name, "table name") + + quoted_db = SnowflakeLoader._quote_identifier(db_name) + + cursor.execute(f""" + SELECT + COLUMN_NAME, + DATA_TYPE, + IS_NULLABLE, + COLUMN_DEFAULT, + COMMENT + FROM {quoted_db}.INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = %s + AND TABLE_NAME = %s + ORDER BY ORDINAL_POSITION; + """, (schema_name, table_name)) + + columns = cursor.fetchall() + columns_info = {} + + # Get primary key information using Snowflake's SHOW command + quoted_table = SnowflakeLoader._quote_identifier(table_name) + quoted_schema = SnowflakeLoader._quote_identifier(schema_name) + cursor.execute(f"SHOW PRIMARY KEYS IN TABLE {quoted_db}.{quoted_schema}.{quoted_table}") + primary_keys = {row['column_name'] for row in cursor.fetchall()} + + # Get foreign key columns using Snowflake's SHOW IMPORTED KEYS + cursor.execute(f"SHOW IMPORTED KEYS IN TABLE {quoted_db}.{quoted_schema}.{quoted_table}") + foreign_keys_cols = {row['fk_column_name'] for row in cursor.fetchall()} + + for col_info in columns: + col_name = col_info['COLUMN_NAME'] + data_type = col_info['DATA_TYPE'] + is_nullable = col_info['IS_NULLABLE'] + column_default = col_info['COLUMN_DEFAULT'] + column_comment = col_info['COMMENT'] + + # Determine key type + if col_name in primary_keys: + key_type = 'PRIMARY KEY' + elif col_name in foreign_keys_cols: + key_type = 'FOREIGN KEY' + else: + key_type = 'NONE' + + # Generate column description + description_parts = [] + if column_comment: + description_parts.append(column_comment) + else: + description_parts.append(f"Column {col_name} of type {data_type}") + + if key_type != 'NONE': + description_parts.append(f"({key_type})") + + if is_nullable == 'NO': + description_parts.append("(NOT NULL)") + + if column_default is not None: + description_parts.append(f"(Default: {column_default})") + + # Extract sample values for the column (stored separately, not in description) + sample_values = SnowflakeLoader.extract_sample_values_for_column( + cursor, table_name, col_name + ) + + columns_info[col_name] = { + 'type': data_type, + 'null': is_nullable, + 'key': key_type, + 'description': ' '.join(description_parts), + 'default': column_default, + 'sample_values': sample_values + } + + return columns_info + + @staticmethod + def extract_foreign_keys( + cursor, db_name: str, schema_name: str, table_name: str + ) -> List[Dict[str, str]]: + """ + Extract foreign key information for a specific table. + + Args: + cursor: Database cursor + db_name: Database name + schema_name: Schema name + table_name: Name of the table + + Returns: + List of foreign key dictionaries + """ + # Validate identifiers to prevent SQL injection + SnowflakeLoader._validate_identifier(db_name, "database name") + SnowflakeLoader._validate_identifier(schema_name, "schema name") + SnowflakeLoader._validate_identifier(table_name, "table name") + + quoted_db = SnowflakeLoader._quote_identifier(db_name) + quoted_schema = SnowflakeLoader._quote_identifier(schema_name) + quoted_table = SnowflakeLoader._quote_identifier(table_name) + + # Use Snowflake's SHOW IMPORTED KEYS for foreign key information + cursor.execute(f"SHOW IMPORTED KEYS IN TABLE {quoted_db}.{quoted_schema}.{quoted_table}") + + foreign_keys = [] + for fk_info in cursor.fetchall(): + foreign_keys.append({ + 'constraint_name': fk_info['fk_name'], + 'column': fk_info['fk_column_name'], + 'referenced_table': fk_info['pk_table_name'], + 'referenced_column': fk_info['pk_column_name'] + }) + + return foreign_keys + + @staticmethod + def extract_relationships( + cursor, db_name: str, schema_name: str + ) -> Dict[str, List[Dict[str, str]]]: + """ + Extract all relationship information from the database. + + Args: + cursor: Database cursor + db_name: Database name + schema_name: Schema name + + Returns: + Dict containing relationship information + """ + # Validate identifiers to prevent SQL injection + SnowflakeLoader._validate_identifier(db_name, "database name") + SnowflakeLoader._validate_identifier(schema_name, "schema name") + + quoted_db = SnowflakeLoader._quote_identifier(db_name) + quoted_schema = SnowflakeLoader._quote_identifier(schema_name) + + # Use Snowflake's SHOW IMPORTED KEYS for each table to get relationships + cursor.execute(f""" + SELECT TABLE_NAME + FROM {quoted_db}.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = %s + AND TABLE_TYPE = 'BASE TABLE' + ORDER BY TABLE_NAME; + """, (schema_name,)) + tables = [row['TABLE_NAME'] for row in cursor.fetchall()] + + relationships = {} + for tbl in tables: + SnowflakeLoader._validate_identifier(tbl, "table name") + quoted_table = SnowflakeLoader._quote_identifier(tbl) + cursor.execute( + f"SHOW IMPORTED KEYS IN TABLE {quoted_db}.{quoted_schema}.{quoted_table}" + ) + for rel_info in cursor.fetchall(): + constraint_name = rel_info['fk_name'] + + if constraint_name not in relationships: + relationships[constraint_name] = [] + + relationships[constraint_name].append({ + 'from': rel_info['fk_table_name'], + 'to': rel_info['pk_table_name'], + 'source_column': rel_info['fk_column_name'], + 'target_column': rel_info['pk_column_name'], + 'note': f'Foreign key constraint: {constraint_name}' + }) + + return relationships + + @staticmethod + def is_schema_modifying_query(sql_query: str) -> Tuple[bool, str]: + """ + Check if a SQL query modifies the database schema. + + Args: + sql_query: The SQL query to check + + Returns: + Tuple of (is_schema_modifying, operation_type) + """ + if not sql_query or not sql_query.strip(): + return False, "" + + # Clean and normalize the query + normalized_query = sql_query.strip().upper() + + # Check for basic DDL operations + first_word = normalized_query.split()[0] if normalized_query.split() else "" + if first_word in SnowflakeLoader.SCHEMA_MODIFYING_OPERATIONS: + # Additional pattern matching for more precise detection + for pattern in SnowflakeLoader.SCHEMA_PATTERNS: + if re.match(pattern, normalized_query, re.IGNORECASE): + return True, first_word + + # If it's a known DDL operation but doesn't match specific patterns, + # still consider it schema-modifying (better safe than sorry) + return True, first_word + + return False, "" + + @staticmethod + async def refresh_graph_schema(graph_id: str, db_url: str) -> Tuple[bool, str]: + """ + Refresh the graph schema by clearing existing data and reloading from the database. + + Args: + graph_id: The graph ID to refresh + db_url: Database connection URL + + Returns: + Tuple of (success, message) + """ + try: + logging.info("Schema modification detected. Refreshing graph schema.") + + # Import here to avoid circular imports + from api.extensions import db # pylint: disable=import-error,import-outside-toplevel + + # Clear existing graph data + # Drop current graph before reloading + graph = db.select_graph(graph_id) + await graph.delete() + + # Extract prefix from graph_id (remove database name part) + # graph_id format is typically "prefix_database_name" + parts = graph_id.split('_') + if len(parts) >= 2: + # Reconstruct prefix by joining all parts except the last one + prefix = '_'.join(parts[:-1]) + else: + prefix = graph_id + + # Reuse the existing load method to reload the schema + success = False + message = "" + async for progress_tuple in SnowflakeLoader.load(prefix, db_url): + success, message = progress_tuple + + if success: + logging.info("Graph schema refreshed successfully.") + return True, message + + logging.error("Schema refresh failed") + return False, "Failed to reload schema" + + except Exception as e: # pylint: disable=broad-exception-caught + # Log the error and return failure + logging.error("Error refreshing graph schema: %s", str(e)) + error_msg = "Error refreshing graph schema" + logging.error(error_msg) + return False, error_msg + + @staticmethod + def execute_sql_query(sql_query: str, db_url: str) -> List[Dict[str, Any]]: + """ + Execute a SQL query on the Snowflake database and return the results. + + Args: + sql_query: The SQL query to execute + db_url: Snowflake connection URL in format: + snowflake://username:password@account/database/schema?warehouse=warehouse_name + + Returns: + List of dictionaries containing the query results + """ + try: + # Parse connection URL + conn_params = SnowflakeLoader._parse_snowflake_url(db_url) + + # Connect to Snowflake database + conn = snowflake.connector.connect(**conn_params) + cursor = conn.cursor(DictCursor) + + # Execute the SQL query + cursor.execute(sql_query) + + # Check if the query returns results (SELECT queries) + if cursor.description is not None: + # This is a SELECT query or similar that returns rows + results = cursor.fetchall() + result_list = [] + for row in results: + # Serialize each value to ensure JSON compatibility + serialized_row = { + key: SnowflakeLoader._serialize_value(value) + for key, value in row.items() + } + result_list.append(serialized_row) + else: + # This is an INSERT, UPDATE, DELETE, or other non-SELECT query + # Return information about the operation + affected_rows = cursor.rowcount + sql_type = sql_query.strip().split()[0].upper() + + if sql_type in ['INSERT', 'UPDATE', 'DELETE']: + result_list = [{ + "operation": sql_type, + "affected_rows": affected_rows, + "status": "success" + }] + else: + # For other types of queries (CREATE, DROP, etc.) + result_list = [{ + "operation": sql_type, + "status": "success" + }] + + # Commit the transaction for write operations + conn.commit() + + # Close database connection + cursor.close() + conn.close() + + return result_list + + except snowflake.connector.Error as e: + # Rollback in case of error + if 'conn' in locals(): + conn.rollback() + cursor.close() + conn.close() + logging.error("Snowflake query execution error: %s", e) + raise SnowflakeQueryError(f"Snowflake query execution error: {str(e)}") from e + except Exception as e: + # Rollback in case of error + if 'conn' in locals(): + conn.rollback() + cursor.close() + conn.close() + logging.error("Error executing SQL query: %s", e) + raise SnowflakeQueryError(f"Error executing SQL query: {str(e)}") from e diff --git a/api/memory/graphiti_tool.py b/api/memory/graphiti_tool.py index 3c944545..00a1b3e6 100644 --- a/api/memory/graphiti_tool.py +++ b/api/memory/graphiti_tool.py @@ -65,6 +65,7 @@ def __init__(self, user_id: str, graph_id: str): # Create Graphiti client with Azure OpenAI configuration self.graphiti_client = create_graphiti_client(falkor_driver) + self.memory_enabled = self.graphiti_client is not None self.user_id = user_id self.graph_id = graph_id @@ -82,6 +83,9 @@ async def create(cls, user_id: str, graph_id: str, use_direct_entities: bool = T """Async factory to construct and initialize the tool.""" self = cls(user_id, graph_id) + if not self.memory_enabled: + return self + await self._ensure_entity_nodes_direct(user_id, graph_id) @@ -301,16 +305,18 @@ async def add_new_memory(self, conversation: Dict[str, Any], history: Tuple[List async def save_query_memory(self, query: str, sql_query: str, success: bool, error: Optional[str] = None) -> bool: """ Save individual query memory directly to the database node. - + Args: query: The user's natural language query sql_query: The generated SQL query success: Whether the query execution was successful error: Error message if the query failed - + Returns: bool: True if memory was saved successfully, False otherwise """ + if not self.memory_enabled: + return False try: database_name = self.graph_id database_node_name = f"Database {database_name}" @@ -396,6 +402,8 @@ async def retrieve_similar_queries(self, query: str, limit: int = 5) -> List[Dic Returns: A list of similar query metadata. """ + if not self.memory_enabled: + return [] try: database_name = self.graph_id @@ -457,10 +465,12 @@ async def search_user_summary(self, limit: int = 5) -> str: Args: query: Natural language query to search for limit: Maximum number of results to return - + Returns: List of user node summaries with metadata """ + if not self.memory_enabled: + return "" try: driver = self.graphiti_client.driver query = """ @@ -508,14 +518,16 @@ async def extract_episode_from_rel(self, rel_result): async def search_database_facts(self, query: str, limit: int = 5, episode_limit: int = 3) -> str: """ Search for database-specific facts and interaction history using database node as center. - + Args: query: Natural language query to search for database facts limit: Maximum number of results to return - + Returns: String containing all relevant database facts with time relevancy information """ + if not self.memory_enabled: + return "" try: driver = self.graphiti_client.driver query = """ @@ -568,15 +580,18 @@ async def search_memories(self, query: str, user_limit: int = 5, database_limit: """ Run both user summary and database facts searches concurrently for better performance. Also builds a comprehensive memory context string for the analysis agent. - + Args: query: Natural language query to search for database facts user_limit: Maximum number of results for user summary search database_limit: Maximum number of results for database facts search - + Returns: - Dict containing user_summary, database_facts, similar_queries, and memory_context + A formatted memory context string combining user summary, database facts, + and similar query history, or empty string if memory is disabled. """ + if not self.memory_enabled: + return "" try: # Run both searches concurrently using asyncio.gather user_summary_task = self.search_user_summary(limit=user_limit) @@ -819,10 +834,10 @@ def create_graphiti_client(falkor_driver: FalkorDriver) -> Graphiti: client=llm_client_azure, ), ) - else: # Fallback to default OpenAI config but use Config's embedding model - # Extract just the model name without provider prefix for Graphiti + elif Config.LLM_PROVIDER == "openai": + # OpenAI provider — use OpenAIEmbedder with configured model embedding_model_name = extract_embedding_model_name(Config.EMBEDDING_MODEL_NAME) - + graphiti_client = Graphiti( graph_driver=falkor_driver, embedder=OpenAIEmbedder( @@ -832,6 +847,16 @@ def create_graphiti_client(falkor_driver: FalkorDriver) -> Graphiti: ) ), ) + else: + # Non-OpenAI/Azure providers (Gemini, Anthropic, Ollama, Cohere): + # Graphiti memory requires OpenAI-compatible embeddings. + # Memory is not supported for these providers. + logging.warning( + "Memory is only supported with Azure or OpenAI providers. " + "Current provider: %s. Memory will be disabled.", + getattr(Config, 'LLM_PROVIDER', 'unknown') + ) + return None return graphiti_client diff --git a/api/routes/settings.py b/api/routes/settings.py new file mode 100644 index 00000000..554081d2 --- /dev/null +++ b/api/routes/settings.py @@ -0,0 +1,114 @@ +"""Settings and configuration routes for the text2sql API.""" + +import logging +from fastapi import APIRouter, Request +from fastapi.responses import JSONResponse +from pydantic import BaseModel +from litellm import completion + +from api.auth.user_management import token_required +from api.routes.tokens import UNAUTHORIZED_RESPONSE + +settings_router = APIRouter(tags=["Settings"]) + + +def _sanitize_for_log(value: str) -> str: + """Remove control characters that could enable log injection.""" + return str(value).replace("\r", "").replace("\n", "").replace("\t", " ") + + +class ValidateKeyRequest(BaseModel): + """Request model for API key validation.""" + api_key: str + vendor: str = "openai" + model: str = "gpt-3.5-turbo" + + +@settings_router.post("/validate-api-key", responses={401: UNAUTHORIZED_RESPONSE}) +@token_required +async def validate_api_key(request: Request, data: ValidateKeyRequest): # pylint: disable=too-many-return-statements,unused-argument + """ + Validate an AI provider API key by making a simple test request. + This endpoint does not store the key, it only validates it. + Supports: openai, google, anthropic + """ + api_key = data.api_key.strip() + vendor = data.vendor.lower() + model = data.model + + if not api_key: + return JSONResponse( + content={"valid": False, "error": "API key is required"}, + status_code=400 + ) + + # Validate vendor — only key-based vendors can be validated via API call + validatable_vendors = ("openai", "anthropic", "gemini", "cohere") + if vendor not in validatable_vendors: + allowed = ", ".join(validatable_vendors) + return JSONResponse( + content={"valid": False, "error": f"Unsupported vendor for key validation. Supported: {allowed}"}, + status_code=400 + ) + + # Validate model is not empty + if not model or not model.strip(): + return JSONResponse( + content={"valid": False, "error": "Model name is required"}, + status_code=400 + ) + + # Validate key format based on vendor + if vendor == "openai" and not api_key.startswith('sk-'): + return JSONResponse( + content={"valid": False, "error": "Invalid OpenAI API key format"}, + status_code=400 + ) + if vendor == "anthropic" and not api_key.startswith('sk-ant-'): + return JSONResponse( + content={"valid": False, "error": "Invalid Anthropic API key format"}, + status_code=400 + ) + + try: + # Construct model name for LiteLLM (vendor/model format) + full_model_name = f"{vendor}/{model}" + + test_response = completion( + model=full_model_name, + messages=[{"role": "user", "content": "test"}], + max_tokens=1, + api_key=api_key, + ) + + # If we get here without exception, the key is valid + if test_response and test_response.choices: + return JSONResponse( + content={"valid": True}, + status_code=200 + ) + return JSONResponse( + content={"valid": False, "error": "Invalid API key"}, + status_code=401 + ) + + except Exception as e: # pylint: disable=broad-except + error_lower = str(e).lower() + logging.warning("API key validation failed for vendor=%s", + _sanitize_for_log(vendor)) + + # Return generic messages — never expose exception details + if "invalid" in error_lower or "authentication" in error_lower: + return JSONResponse( + content={"valid": False, "error": "Invalid API key"}, + status_code=401 + ) + if "quota" in error_lower or "rate" in error_lower: + return JSONResponse( + content={"valid": False, "error": "API quota exceeded or rate limited"}, + status_code=429 + ) + return JSONResponse( + content={"valid": False, "error": "Failed to validate API key"}, + status_code=500 + ) diff --git a/api/utils.py b/api/utils.py index 845ca604..e6979876 100644 --- a/api/utils.py +++ b/api/utils.py @@ -98,7 +98,8 @@ def create_combined_description( # pylint: disable=too-many-locals if isinstance(batch_response, Exception): table_info[table_name]["description"] = table_name else: - content = batch_response.choices[0].message["content"].strip() + msg_content = batch_response.choices[0].message["content"] + content = msg_content.strip() if msg_content else table_name table_info[table_name]["description"] = content return table_info diff --git a/app/package-lock.json b/app/package-lock.json index 33c4e227..13db7eed 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -8,8 +8,8 @@ "name": "queryweaver-app", "version": "0.1.0", "dependencies": { - "@falkordb/canvas": "^0.0.41", - "@hookform/resolvers": "^3.10.0", + "@falkordb/canvas": "^0.0.45", + "@hookform/resolvers": "^5.2.2", "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-aspect-ratio": "^1.1.7", @@ -46,7 +46,7 @@ "date-fns": "^3.6.0", "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", - "lucide-react": "^0.462.0", + "lucide-react": "^0.577.0", "next-themes": "^0.3.0", "preact": "^10.28.4", "react": "^18.3.1", @@ -54,7 +54,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.71.2", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", + "react-router-dom": "^7.13.2", "recharts": "^2.15.4", "sonner": "^1.7.4", "tailwind-merge": "^2.6.0", @@ -74,10 +74,10 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^17.3.0", - "postcss": "^8.5.6", + "postcss": "^8.5.8", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", - "typescript-eslint": "^8.38.0", + "typescript-eslint": "^8.57.0", "vite": "^7.3.0" } }, @@ -674,9 +674,9 @@ } }, "node_modules/@falkordb/canvas": { - "version": "0.0.41", - "resolved": "https://registry.npmjs.org/@falkordb/canvas/-/canvas-0.0.41.tgz", - "integrity": "sha512-r2DC2Mo9naASr3DDmVKCQVY/S50Hn2bGlmKaFxDRFcn5btiuuvPBqJ3krtC+uz5ZPmad8EoXRRlcQ6KwKJhUZw==", + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@falkordb/canvas/-/canvas-0.0.45.tgz", + "integrity": "sha512-BIb24rfnaLCxO2UH7L4cNYLikmOTXlVR4I/FIpEG/d9ZEp3F+9U/ugaLYOcYe1pmZzT72YnzWUuvdhNE3zK99A==", "license": "MIT", "dependencies": { "d3": "^7.9.0", @@ -724,10 +724,15 @@ "license": "MIT" }, "node_modules/@hookform/resolvers": { - "version": "3.10.0", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", + "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, "peerDependencies": { - "react-hook-form": "^7.0.0" + "react-hook-form": "^7.55.0" } }, "node_modules/@humanfs/core": { @@ -2333,13 +2338,6 @@ "version": "1.1.1", "license": "MIT" }, - "node_modules/@remix-run/router": { - "version": "1.23.2", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "dev": true, @@ -2695,6 +2693,12 @@ "win32" ] }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/core": { "version": "1.15.8", "dev": true, @@ -3192,15 +3196,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.53.0", - "@typescript-eslint/type-utils": "8.53.0", - "@typescript-eslint/utils": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -3213,13 +3219,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.53.0", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -3227,14 +3235,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.53.0", - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "engines": { @@ -3245,17 +3255,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.53.0", - "@typescript-eslint/types": "^8.53.0", + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "engines": { @@ -3270,12 +3282,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3286,7 +3300,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", "engines": { @@ -3301,13 +3317,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0", - "@typescript-eslint/utils": "8.53.0", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -3319,12 +3337,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true, "license": "MIT", "engines": { @@ -3336,16 +3356,18 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.53.0", - "@typescript-eslint/tsconfig-utils": "8.53.0", - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0", + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" @@ -3361,39 +3383,56 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.53.0", - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0" + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3403,17 +3442,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3423,6 +3464,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.11.0", "dev": true, @@ -3464,7 +3518,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -3663,9 +3719,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001775", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz", - "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==", + "version": "1.0.30001777", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", + "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", "dev": true, "funding": [ { @@ -3803,6 +3859,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -4670,7 +4739,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -4765,9 +4836,9 @@ } }, "node_modules/globals": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", - "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true, "license": "MIT", "engines": { @@ -5014,15 +5085,15 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash-es": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", - "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "license": "MIT" }, "node_modules/lodash.merge": { @@ -5041,10 +5112,12 @@ } }, "node_modules/lucide-react": { - "version": "0.462.0", + "version": "0.577.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz", + "integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==", "license": "ISC", "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/merge2": { @@ -5227,7 +5300,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -5251,7 +5326,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "funding": [ { "type": "opencollective", @@ -5566,31 +5643,41 @@ } }, "node_modules/react-router": { - "version": "6.30.3", + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz", + "integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.2" + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/react-router-dom": { - "version": "6.30.3", + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz", + "integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.2", - "react-router": "6.30.3" + "react-router": "7.13.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/react-smooth": { @@ -5811,7 +5898,9 @@ } }, "node_modules/semver": { - "version": "7.7.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -5821,6 +5910,12 @@ "node": ">=10" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "dev": true, @@ -6032,7 +6127,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -6052,7 +6149,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -6094,14 +6193,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.53.0", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.53.0", - "@typescript-eslint/parser": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0", - "@typescript-eslint/utils": "8.53.0" + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6111,7 +6212,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, @@ -6328,7 +6429,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/app/package.json b/app/package.json index 6322ceab..ddcdb637 100644 --- a/app/package.json +++ b/app/package.json @@ -11,8 +11,8 @@ "preview": "vite preview" }, "dependencies": { - "@falkordb/canvas": "^0.0.41", - "@hookform/resolvers": "^3.10.0", + "@falkordb/canvas": "^0.0.45", + "@hookform/resolvers": "^5.2.2", "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-aspect-ratio": "^1.1.7", @@ -49,7 +49,7 @@ "date-fns": "^3.6.0", "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", - "lucide-react": "^0.462.0", + "lucide-react": "^0.577.0", "next-themes": "^0.3.0", "preact": "^10.28.4", "react": "^18.3.1", @@ -57,7 +57,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.71.2", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", + "react-router-dom": "^7.13.2", "recharts": "^2.15.4", "sonner": "^1.7.4", "tailwind-merge": "^2.6.0", @@ -77,10 +77,10 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^17.3.0", - "postcss": "^8.5.6", + "postcss": "^8.5.8", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", - "typescript-eslint": "^8.38.0", + "typescript-eslint": "^8.57.0", "vite": "^7.3.0" }, "overrides": { diff --git a/app/src/App.tsx b/app/src/App.tsx index 25699055..1746e1be 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import { AuthProvider } from "@/contexts/AuthContext"; import { DatabaseProvider } from "@/contexts/DatabaseContext"; +import { SettingsProvider } from "@/contexts/SettingsContext"; import { ChatProvider } from "@/contexts/ChatContext"; import Index from "./pages/Index"; import Settings from "./pages/Settings"; @@ -15,19 +16,21 @@ const App = () => ( - - - - - - } /> - } /> - {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} - } /> - - - - + + + + + + + } /> + } /> + {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} + } /> + + + + + diff --git a/app/src/components/chat/ChatInterface.tsx b/app/src/components/chat/ChatInterface.tsx index 75548563..6f43691a 100644 --- a/app/src/components/chat/ChatInterface.tsx +++ b/app/src/components/chat/ChatInterface.tsx @@ -3,6 +3,7 @@ import { cn } from "@/lib/utils"; import { useToast } from "@/components/ui/use-toast"; import { useDatabase } from "@/contexts/DatabaseContext"; import { useAuth } from "@/contexts/AuthContext"; +import { useSettings } from "@/contexts/SettingsContext"; import { useChat } from "@/contexts/ChatContext"; import LoadingSpinner from "@/components/ui/loading-spinner"; import { Skeleton } from "@/components/ui/skeleton"; @@ -10,6 +11,8 @@ import ChatMessage from "./ChatMessage"; import QueryInput from "./QueryInput"; import SuggestionCards from "../SuggestionCards"; import { ChatService } from "@/services/chat"; +import type { ConfirmRequest } from "@/types/api"; +import { getVendorPrefix } from "@/utils/vendorConfig"; interface ChatMessageData { id: string; @@ -53,6 +56,7 @@ const ChatInterface = ({ }: ChatInterfaceProps) => { const { toast } = useToast(); const { selectedGraph } = useDatabase(); + const { vendor, apiKey, modelName, isApiKeyValid } = useSettings(); const { messages, setMessages, conversationHistory, isProcessing, setIsProcessing } = useChat(); const messagesEndRef = useRef(null); const chatContainerRef = useRef(null); @@ -146,13 +150,15 @@ const ChatInterface = ({ explanation?: string; isValid?: boolean; } = {}; - // Stream the query for await (const message of ChatService.streamQuery({ query, database: selectedGraph.id, history: historySnapshot, - use_user_rules: useRulesFromDatabase, // Backend fetches from DB when true + customApiKey: isApiKeyValid ? apiKey : undefined, + customModel: isApiKeyValid ? modelName : undefined, + customVendor: isApiKeyValid ? vendor : undefined, + use_user_rules: useRulesFromDatabase, use_memory: useMemory, })) { @@ -323,15 +329,27 @@ const ChatInterface = ({ let finalContent = ""; let queryResults: any[] | null = null; + // Build confirm request with custom credentials if available + const confirmRequest: ConfirmRequest = { + sql_query: confirmMessage.confirmationData.sqlQuery, + confirmation: 'CONFIRM', + chat: confirmMessage.confirmationData.chatHistory, + use_user_rules: useRulesFromDatabase, + }; + if (isApiKeyValid && apiKey) { + confirmRequest.custom_api_key = apiKey; + if (modelName && vendor) { + const vendorPrefix = getVendorPrefix(vendor); + confirmRequest.custom_model = modelName.startsWith(`${vendorPrefix}/`) + ? modelName + : `${vendorPrefix}/${modelName}`; + } + } + // Stream the confirmation response for await (const message of ChatService.streamConfirmOperation( selectedGraph.id, - { - sql_query: confirmMessage.confirmationData.sqlQuery, - confirmation: 'CONFIRM', - chat: confirmMessage.confirmationData.chatHistory, - use_user_rules: useRulesFromDatabase, // Backend fetches from DB when true - } + confirmRequest )) { if (message.type === 'status' || message.type === 'reasoning' || message.type === 'reasoning_step') { // Add reasoning steps diff --git a/app/src/components/layout/Sidebar.tsx b/app/src/components/layout/Sidebar.tsx index bdabc0fc..56acef9e 100644 --- a/app/src/components/layout/Sidebar.tsx +++ b/app/src/components/layout/Sidebar.tsx @@ -6,7 +6,7 @@ import { BookOpen, LifeBuoy, Waypoints, - Settings, + Sliders, } from 'lucide-react'; import { Tooltip, @@ -141,7 +141,7 @@ const Sidebar = ({ className, onSchemaClick, isSchemaOpen, isCollapsed = false, diff --git a/app/src/components/modals/DatabaseModal.tsx b/app/src/components/modals/DatabaseModal.tsx index 35feae26..e3a7f47a 100644 --- a/app/src/components/modals/DatabaseModal.tsx +++ b/app/src/components/modals/DatabaseModal.tsx @@ -29,6 +29,15 @@ const DatabaseModal = ({ open, onOpenChange }: DatabaseModalProps) => { const [database, setDatabase] = useState(""); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); + const [schema, setSchema] = useState(""); + const [schemaError, setSchemaError] = useState(""); + // Snowflake-specific fields + const [account, setAccount] = useState(""); + const [snowflakeSchema, setSnowflakeSchema] = useState("PUBLIC"); + const [warehouse, setWarehouse] = useState("COMPUTE_WH"); + const [authMode, setAuthMode] = useState<'password' | 'keypair'>('password'); + const [privateKey, setPrivateKey] = useState(""); + const [privateKeyPassphrase, setPrivateKeyPassphrase] = useState(""); const [isConnecting, setIsConnecting] = useState(false); const [connectionSteps, setConnectionSteps] = useState([]); const { refreshGraphs } = useDatabase(); @@ -73,13 +82,32 @@ const DatabaseModal = ({ open, onOpenChange }: DatabaseModalProps) => { return; } } else { - if (!selectedDatabase || !host || !port || !database || !username) { - toast({ - title: "Missing Information", - description: "Please fill in all required fields", - variant: "destructive", - }); - return; + if (selectedDatabase === 'snowflake') { + if (!account || !database || !username) { + toast({ + title: "Missing Information", + description: "Please fill in all required fields (account, database, username)", + variant: "destructive", + }); + return; + } + if (authMode === 'keypair' && !privateKey) { + toast({ + title: "Missing Information", + description: "Please paste your private key in PEM format", + variant: "destructive", + }); + return; + } + } else { + if (!selectedDatabase || !host || !port || !database || !username) { + toast({ + title: "Missing Information", + description: "Please fill in all required fields", + variant: "destructive", + }); + return; + } } } @@ -90,11 +118,37 @@ const DatabaseModal = ({ open, onOpenChange }: DatabaseModalProps) => { // Build the connection URL let dbUrl = connectionUrl; if (connectionMode === 'manual') { - const protocol = selectedDatabase === 'mysql' ? 'mysql' : 'postgresql'; - const builtUrl = new URL(`${protocol}://${host}:${port}/${database}`); - builtUrl.username = username; - builtUrl.password = password; - dbUrl = builtUrl.toString(); + if (selectedDatabase === 'snowflake') { + // Build Snowflake URL: snowflake://user@account/database/schema?warehouse=WH + const builtUrl = new URL(`snowflake://${account}/${database}/${snowflakeSchema}`); + builtUrl.username = username; + if (authMode === 'keypair' && privateKey) { + // Base64-encode the PEM key for safe URL transport + builtUrl.searchParams.set('private_key', btoa(privateKey)); + if (privateKeyPassphrase) { + builtUrl.searchParams.set('private_key_passphrase', privateKeyPassphrase); + } + } else { + builtUrl.password = password; + } + builtUrl.searchParams.set('warehouse', warehouse); + dbUrl = builtUrl.toString(); + } else { + const protocol = selectedDatabase === 'mysql' ? 'mysql' : 'postgresql'; + const builtUrl = new URL(`${protocol}://${host}:${port}/${database}`); + builtUrl.username = username; + builtUrl.password = password; + + // Append schema option for PostgreSQL if provided + if (selectedDatabase === 'postgresql' && schema.trim()) { + if (/[^a-zA-Z0-9_]/.test(schema.trim())) { + throw new Error('Schema name can only contain letters, digits, and underscores'); + } + builtUrl.searchParams.set('options', `-csearch_path=${schema.trim()}`); + } + + dbUrl = builtUrl.toString(); + } } // Make streaming request @@ -177,6 +231,14 @@ const DatabaseModal = ({ open, onOpenChange }: DatabaseModalProps) => { setDatabase(""); setUsername(""); setPassword(""); + setSchema(""); + setSchemaError(""); + setAccount(""); + setSnowflakeSchema("PUBLIC"); + setWarehouse("COMPUTE_WH"); + setAuthMode('password'); + setPrivateKey(""); + setPrivateKeyPassphrase(""); setConnectionSteps([]); }, 1000); } else { @@ -239,7 +301,7 @@ const DatabaseModal = ({ open, onOpenChange }: DatabaseModalProps) => { Connect to Database - Connect to PostgreSQL or MySQL database using a connection URL or manual entry.{" "} + Connect to PostgreSQL, MySQL, or Snowflake database using a connection URL or manual entry.{" "} { MySQL + +
+
+ Snowflake +
+
@@ -315,75 +383,248 @@ const DatabaseModal = ({ open, onOpenChange }: DatabaseModalProps) => { placeholder={ selectedDatabase === 'postgresql' ? 'postgresql://username:password@host:5432/database' - : 'mysql://username:password@host:3306/database' + : selectedDatabase === 'mysql' + ? 'mysql://username:password@host:3306/database' + : 'snowflake://username:password@account/database/schema?warehouse=warehouse_name' } value={connectionUrl} onChange={(e) => setConnectionUrl(e.target.value)} className="bg-muted border-border font-mono text-sm focus-visible:ring-purple-500" />

- Enter your database connection string + {selectedDatabase === 'snowflake' + ? 'Enter your Snowflake connection string (schema defaults to PUBLIC, warehouse to COMPUTE_WH)' + : 'Enter your database connection string'}

)} {selectedDatabase && connectionMode === 'manual' && ( <> -
- - setHost(e.target.value)} - className="bg-muted border-border focus-visible:ring-purple-500" - /> -
+ {selectedDatabase === 'snowflake' ? ( + <> +
+ + setAccount(e.target.value)} + className="bg-muted border-border focus-visible:ring-purple-500" + /> +

+ Your Snowflake account identifier (e.g., myorg-account) +

+
-
- - setPort(e.target.value)} - className="bg-muted border-border focus-visible:ring-purple-500" - /> -
+
+ + setDatabase(e.target.value)} + className="bg-muted border-border focus-visible:ring-purple-500" + /> +
-
- - setDatabase(e.target.value)} - className="bg-muted border-border focus-visible:ring-purple-500" - /> -
+
+ + setSnowflakeSchema(e.target.value)} + className="bg-muted border-border focus-visible:ring-purple-500" + /> +

+ Defaults to PUBLIC if not specified +

+
-
- - setUsername(e.target.value)} - className="bg-muted border-border focus-visible:ring-purple-500" - /> -
+
+ + setWarehouse(e.target.value)} + className="bg-muted border-border focus-visible:ring-purple-500" + /> +

+ Defaults to COMPUTE_WH if not specified +

+
-
- - setPassword(e.target.value)} - className="bg-muted border-border focus-visible:ring-purple-500" - /> -
+
+ + setUsername(e.target.value)} + className="bg-muted border-border focus-visible:ring-purple-500" + /> +
+ + {/* Auth Mode Toggle */} +
+ +
+ + +
+
+ + {authMode === 'password' ? ( +
+ + setPassword(e.target.value)} + className="bg-muted border-border focus-visible:ring-purple-500" + /> +
+ ) : ( + <> +
+ +