diff --git a/.github/workflows/publish-mcp-registry.yml b/.github/workflows/publish-mcp-registry.yml index 8e1df3b..8da4a63 100644 --- a/.github/workflows/publish-mcp-registry.yml +++ b/.github/workflows/publish-mcp-registry.yml @@ -13,7 +13,6 @@ on: options: - all - qiskit - - code-assistant - docs - runtime - transpiler @@ -49,35 +48,6 @@ jobs: working-directory: ./qiskit-mcp-server run: mcp-publisher publish - publish-code-assistant-mcp-registry: - if: | - (github.event_name == 'release' && contains(github.ref, 'code-assistant')) || - (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'all' || github.event.inputs.package == 'code-assistant')) - runs-on: ubuntu-latest - permissions: - id-token: write # Required for GitHub OIDC authentication - contents: read - - steps: - - uses: actions/checkout@v4 - - - name: Wait for PyPI availability - working-directory: ./qiskit-code-assistant-mcp-server - run: ${{ github.workspace }}/.github/scripts/wait-for-pypi.sh - - - name: Download mcp-publisher - run: | - curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" | tar xz mcp-publisher - sudo mv mcp-publisher /usr/local/bin/ - mcp-publisher --help - - - name: Login to MCP Registry (OIDC) - run: mcp-publisher login github-oidc - - - name: Publish to MCP Registry - working-directory: ./qiskit-code-assistant-mcp-server - run: mcp-publisher publish - publish-runtime-mcp-registry: if: | (github.event_name == 'release' && contains(github.ref, 'runtime')) || diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 28ee7b4..1d7926c 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,6 @@ on: - all - meta-package - qiskit - - code-assistant - docs - runtime - transpiler @@ -60,45 +59,6 @@ jobs: packages-dir: qiskit-mcp-server/dist/ verbose: true - publish-code-assistant: - if: | - (github.event_name == 'release' && contains(github.ref, 'code-assistant')) || - (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'all' || github.event.inputs.package == 'code-assistant')) - runs-on: ubuntu-latest - permissions: - id-token: write # Required for trusted publishing - contents: read - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - version: "latest" - - - name: Set up Python - run: uv python install 3.12 - - - name: Build package - working-directory: ./qiskit-code-assistant-mcp-server - run: | - uv build --out-dir dist - - - name: Verify build output - run: | - ls -la qiskit-code-assistant-mcp-server/dist/ - if [ ! -d "qiskit-code-assistant-mcp-server/dist" ] || [ -z "$(ls -A qiskit-code-assistant-mcp-server/dist)" ]; then - echo "ERROR: Build did not produce any distribution files" - exit 1 - fi - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - packages-dir: qiskit-code-assistant-mcp-server/dist/ - verbose: true - publish-runtime: if: | (github.event_name == 'release' && contains(github.ref, 'runtime')) || @@ -258,12 +218,11 @@ jobs: publish-meta-package: runs-on: ubuntu-latest # Meta-package should be published after individual servers - needs: [publish-qiskit, publish-code-assistant, publish-docs, publish-runtime, publish-transpiler, publish-gym] + needs: [publish-qiskit, publish-docs, publish-runtime, publish-transpiler, publish-gym] # Allow meta-package to run even if individual packages were skipped (already published) if: | always() && (needs.publish-qiskit.result == 'success' || needs.publish-qiskit.result == 'skipped') && - (needs.publish-code-assistant.result == 'success' || needs.publish-code-assistant.result == 'skipped') && (needs.publish-docs.result == 'success' || needs.publish-docs.result == 'skipped') && (needs.publish-runtime.result == 'success' || needs.publish-runtime.result == 'skipped') && (needs.publish-transpiler.result == 'success' || needs.publish-transpiler.result == 'skipped') && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 077288b..981e9ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,8 +24,7 @@ jobs: - name: Install dependencies run: | - cd qiskit-code-assistant-mcp-server && uv sync --group dev - cd ../qiskit-docs-mcp-server && uv sync --group dev + cd qiskit-docs-mcp-server && uv sync --group dev cd ../qiskit-ibm-runtime-mcp-server && uv sync --group dev cd ../qiskit-ibm-transpiler-mcp-server && uv sync --group dev cd ../qiskit-mcp-server && uv sync --group dev @@ -33,8 +32,7 @@ jobs: - name: Run ruff linting run: | - cd qiskit-code-assistant-mcp-server && uv run ruff check src tests - cd ../qiskit-docs-mcp-server && uv run ruff check src tests + cd qiskit-docs-mcp-server && uv run ruff check src tests cd ../qiskit-ibm-runtime-mcp-server && uv run ruff check src tests cd ../qiskit-ibm-transpiler-mcp-server && uv run ruff check src tests cd ../qiskit-mcp-server && uv run ruff check src tests @@ -42,8 +40,7 @@ jobs: - name: Run ruff formatting check run: | - cd qiskit-code-assistant-mcp-server && uv run ruff format --check src tests - cd ../qiskit-docs-mcp-server && uv run ruff format --check src tests + cd qiskit-docs-mcp-server && uv run ruff format --check src tests cd ../qiskit-ibm-runtime-mcp-server && uv run ruff format --check src tests cd ../qiskit-ibm-transpiler-mcp-server && uv run ruff format --check src tests cd ../qiskit-mcp-server && uv run ruff format --check src tests @@ -51,8 +48,7 @@ jobs: - name: Run type checking run: | - cd qiskit-code-assistant-mcp-server && uv run mypy --config-file ../mypy.ini src - cd ../qiskit-docs-mcp-server && uv run mypy --config-file ../mypy.ini src + cd qiskit-docs-mcp-server && uv run mypy --config-file ../mypy.ini src cd ../qiskit-ibm-runtime-mcp-server && uv run mypy --config-file ../mypy.ini src cd ../qiskit-ibm-transpiler-mcp-server && uv run mypy --config-file ../mypy.ini src cd ../qiskit-mcp-server && uv run mypy --config-file ../mypy.ini src @@ -60,51 +56,12 @@ jobs: - name: Run security checks run: | - cd qiskit-code-assistant-mcp-server && uv run bandit -c pyproject.toml -r src - cd ../qiskit-docs-mcp-server && uv run bandit -c pyproject.toml -r src + cd qiskit-docs-mcp-server && uv run bandit -c pyproject.toml -r src cd ../qiskit-ibm-runtime-mcp-server && uv run bandit -c pyproject.toml -r src cd ../qiskit-ibm-transpiler-mcp-server && uv run bandit -c pyproject.toml -r src cd ../qiskit-mcp-server && uv run bandit -c pyproject.toml -r src cd ../qiskit-gym-mcp-server && uv run bandit -c pyproject.toml -r src - test-code-assistant: - runs-on: ubuntu-latest - needs: lint - strategy: - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - version: "latest" - enable-cache: true - - - name: Set up Python ${{ matrix.python-version }} - run: uv python install ${{ matrix.python-version }} - - - name: Install dependencies for Code Assistant server - working-directory: ./qiskit-code-assistant-mcp-server - run: | - uv sync --group dev --group test - - - name: Run tests for Code Assistant server - working-directory: ./qiskit-code-assistant-mcp-server - env: - QISKIT_IBM_TOKEN: ${{ secrets.QISKIT_IBM_TOKEN }} - run: | - uv run pytest tests/ -v --cov=src --cov-report=xml --cov-report=term-missing - - - name: Upload coverage for Code Assistant server - uses: codecov/codecov-action@v3 - with: - file: ./qiskit-code-assistant-mcp-server/coverage.xml - flags: code-assistant - name: code-assistant-coverage - test-runtime: runs-on: ubuntu-latest needs: lint diff --git a/AGENTS.md b/AGENTS.md index a150bcd..0aa6ce0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,12 +36,11 @@ qiskit-mcp-servers is a collection of **Model Context Protocol (MCP)** servers t ### Repository Structure -This is a **monorepo** using uv workspace containing six independent MCP servers: +This is a **monorepo** using uv workspace containing five independent MCP servers: ``` qiskit-mcp-servers/ ├── qiskit-mcp-server/ # Core Qiskit transpilation -├── qiskit-code-assistant-mcp-server/ # AI code completion ├── qiskit-docs-mcp-server/ # Documentation retrieval ├── qiskit-ibm-runtime-mcp-server/ # IBM Quantum cloud services ├── qiskit-ibm-transpiler-mcp-server/ # AI-powered transpilation @@ -72,7 +71,6 @@ The root `pyproject.toml` defines a uv workspace: [tool.uv.workspace] members = [ "qiskit-mcp-server", - "qiskit-code-assistant-mcp-server", "qiskit-docs-mcp-server", "qiskit-ibm-runtime-mcp-server", "qiskit-ibm-transpiler-mcp-server", @@ -82,9 +80,11 @@ members = [ The root package is also a **meta-package** that installs all servers: ```bash -pip install qiskit-mcp-servers # Installs all six servers +pip install qiskit-mcp-servers # Installs all five servers ``` +> **Removed:** A `qiskit-code-assistant-mcp-server` previously lived in this monorepo. It was removed because the underlying Qiskit Code Assistant service has been [discontinued by IBM Quantum](https://quantum.cloud.ibm.com/announcements/en/product-updates/2026-04-28-qiskit-code-assistant-service-to-sunset). The PyPI package `qiskit-code-assistant-mcp-server` is archived. Do not re-introduce references to it. + ### Component Structure Each MCP server follows this standard structure: @@ -153,43 +153,7 @@ Each MCP server follows this standard structure: --- -### 2. Qiskit Code Assistant MCP Server - -**Purpose**: Intelligent quantum code completion and assistance - -**Directory**: [`qiskit-code-assistant-mcp-server/`](qiskit-code-assistant-mcp-server/) - -**Core Files**: -- `server.py`: FastMCP server with tool/resource definitions -- `qca.py`: Qiskit Code Assistant API integration (async) -- `constants.py`: API endpoints and configuration -- `utils.py`: HTTP client management and utilities - -**Tools Provided**: -| Tool | Description | -|------|-------------| -| `get_completion_tool` | Get code completion for quantum code prompts | -| `get_rag_completion_tool` | RAG-based completion with documentation context | -| `accept_completion_tool` | Mark a completion as accepted (telemetry) | -| `accept_model_disclaimer_tool` | Accept disclaimer for a model | - -**Resources Provided**: -| Resource URI | Description | -|--------------|-------------| -| `qca://status` | Service status and connection info | -| `qca://models` | List available models | -| `qca://model/{model_id}` | Specific model information | -| `qca://disclaimer/{model_id}` | Model disclaimer information | - -**Environment Variables**: -- `QISKIT_IBM_TOKEN`: IBM Quantum API token (required) -- `QCA_TOOL_API_BASE`: API base URL (default: https://qiskit-code-assistant.quantum.ibm.com) -- `QCA_TOOL_MODEL_NAME`: Model to use for completions -- `QCA_MCP_DEBUG_LEVEL`: Logging level (default: INFO) - ---- - -### 3. Qiskit Documentation MCP Server +### 2. Qiskit Documentation MCP Server **Purpose**: Query and retrieve Qiskit documentation, guides, and API references @@ -237,7 +201,7 @@ Each MCP server follows this standard structure: --- -### 4. Qiskit IBM Runtime MCP Server +### 3. Qiskit IBM Runtime MCP Server **Purpose**: Complete access to IBM Quantum cloud services @@ -304,7 +268,7 @@ Each MCP server follows this standard structure: --- -### 5. Qiskit IBM Transpiler MCP Server +### 4. Qiskit IBM Transpiler MCP Server **Purpose**: AI-powered circuit transpilation with routing and synthesis @@ -341,7 +305,7 @@ Each MCP server follows this standard structure: --- -### 6. Qiskit Gym MCP Server +### 5. Qiskit Gym MCP Server **Purpose**: Reinforcement learning-based quantum circuit synthesis @@ -467,17 +431,6 @@ AI Assistant → MCP Client → transpile_circuit_tool Transpiled circuit (QPY format) ``` -### Qiskit Code Assistant Server -``` -AI Assistant → MCP Client → get_completion_tool - ↓ - qca.py (async functions) - ↓ - IBM Qiskit Code Assistant API - ↓ - Code completion response -``` - ### Qiskit Documentation Server ``` AI Assistant → MCP Client → search_docs_tool / get_page_tool @@ -559,7 +512,6 @@ AI Assistant → MCP Client → create_*_env_tool → start_training_tool ```bash # Run specific servers uv run qiskit-mcp-server - uv run qiskit-code-assistant-mcp-server uv run qiskit-ibm-runtime-mcp-server uv run qiskit-ibm-transpiler-mcp-server uv run qiskit-docs-mcp-server @@ -830,7 +782,6 @@ When adding a new MCP server, you must update the following GitHub configuration | Server | Release Tag Pattern | |--------|---------------------| | qiskit-mcp-server | `qiskit-v*` | - | qiskit-code-assistant-mcp-server | `code-assistant*` | | qiskit-ibm-runtime-mcp-server | `runtime*` | | qiskit-ibm-transpiler-mcp-server | `transpiler*` | | qiskit-docs-mcp-server | `docs*` | @@ -978,7 +929,7 @@ Each server is also published to the [MCP Registry](https://registry.modelcontex **Automated publishing** (recommended): - Publishing happens automatically via GitHub Actions when a release is created -- Uses the same release tags as PyPI (`qiskit-v*`, `code-assistant*`, `runtime*`, `transpiler*`, `docs*`, `gym*`) +- Uses the same release tags as PyPI (`qiskit-v*`, `runtime*`, `transpiler*`, `docs*`, `gym*`) - Uses GitHub OIDC authentication (no secrets required) **Manual publishing**: @@ -1016,7 +967,6 @@ Each server has a `server.json` file that defines its MCP Registry metadata: ### Server-Specific Documentation - `qiskit-mcp-server/README.md`: Core Qiskit server docs -- `qiskit-code-assistant-mcp-server/README.md`: Code Assistant server docs - `qiskit-ibm-runtime-mcp-server/README.md`: IBM Runtime server docs - `qiskit-ibm-transpiler-mcp-server/README.md`: IBM Transpiler server docs - `qiskit-docs-mcp-server/README.md`: Documentation server docs @@ -1155,25 +1105,6 @@ qiskit-mcp-servers/ │ ├── LICENSE │ ├── README.md │ └── run_tests.sh -├── qiskit-code-assistant-mcp-server/ -│ ├── src/qiskit_code_assistant_mcp_server/ -│ │ ├── __init__.py -│ │ ├── server.py # FastMCP server -│ │ ├── qca.py # Core async functions -│ │ ├── constants.py # Configuration -│ │ └── utils.py # Utilities -│ ├── tests/ -│ │ ├── conftest.py -│ │ └── test_*.py -│ ├── examples/ -│ │ ├── README.md -│ │ ├── langchain_agent.ipynb -│ │ └── langchain_agent.py -│ ├── pyproject.toml -│ ├── pytest.ini -│ ├── LICENSE -│ ├── README.md -│ └── run_tests.sh ├── qiskit-docs-mcp-server/ │ ├── src/qiskit_docs_mcp_server/ │ │ ├── __init__.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a77ed34..3112193 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,8 +29,6 @@ Before you begin, ensure you have the following installed: ```bash cd qiskit-mcp-server # OR - cd qiskit-code-assistant-mcp-server - # OR cd qiskit-ibm-runtime-mcp-server # OR cd qiskit-ibm-transpiler-mcp-server @@ -55,8 +53,6 @@ Before you begin, ensure you have the following installed: ```bash uv run qiskit-mcp-server # OR - uv run qiskit-code-assistant-mcp-server - # OR uv run qiskit-ibm-runtime-mcp-server # OR uv run qiskit-ibm-transpiler-mcp-server @@ -68,7 +64,7 @@ Before you begin, ensure you have the following installed: 6. **Test interactively with MCP Inspector** (requires Node.js): ```bash - npx @modelcontextprotocol/inspector uv run qiskit-code-assistant-mcp-server + npx @modelcontextprotocol/inspector uv run qiskit-mcp-server ``` ## Contributing Workflow @@ -185,7 +181,6 @@ This is a monorepo with multiple independent MCP servers: ``` qiskit-mcp-servers/ ├── qiskit-mcp-server/ # Core Qiskit server with circuit utilities -├── qiskit-code-assistant-mcp-server/ # AI code completion server ├── qiskit-ibm-runtime-mcp-server/ # IBM Quantum cloud services ├── qiskit-ibm-transpiler-mcp-server/ # AI-powered transpilation ├── qiskit-docs-mcp-server/ # Documentation retrieval diff --git a/PUBLISHING.md b/PUBLISHING.md index bf99b04..c88d96e 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -7,12 +7,13 @@ This guide covers how to publish the Qiskit MCP servers to PyPI and the MCP Regi This repository contains multiple PyPI packages: 1. **qiskit-mcp-server** - MCP server for Qiskit quantum computing capabilities with circuit serialization utilities -2. **qiskit-code-assistant-mcp-server** - MCP server for Qiskit Code Assistant -3. **qiskit-ibm-runtime-mcp-server** - MCP server for IBM Quantum Runtime -4. **qiskit-ibm-transpiler-mcp-server** - MCP server for transpilation using the AI-powered transpiler passes. -5. **qiskit-docs-mcp-server** - MCP server for Qiskit documentation retrieval and search -6. **qiskit-gym-mcp-server** - MCP server for qiskit-gym reinforcement learning circuit synthesis -7. **qiskit-mcp-servers** - Meta-package that installs all MCP servers +2. **qiskit-ibm-runtime-mcp-server** - MCP server for IBM Quantum Runtime +3. **qiskit-ibm-transpiler-mcp-server** - MCP server for transpilation using the AI-powered transpiler passes. +4. **qiskit-docs-mcp-server** - MCP server for Qiskit documentation retrieval and search +5. **qiskit-gym-mcp-server** - MCP server for qiskit-gym reinforcement learning circuit synthesis +6. **qiskit-mcp-servers** - Meta-package that installs all MCP servers + +> **Note:** A `qiskit-code-assistant-mcp-server` package previously lived in this monorepo. It was removed because the underlying Qiskit Code Assistant service has been [discontinued by IBM Quantum](https://quantum.cloud.ibm.com/announcements/en/product-updates/2026-04-28-qiskit-code-assistant-service-to-sunset). The PyPI package is archived. ### Meta-Package @@ -24,7 +25,6 @@ pip install qiskit-mcp-servers # Or install individual servers via extras pip install qiskit-mcp-servers[qiskit] # Only Qiskit -pip install qiskit-mcp-servers[code-assistant] # Only Code Assistant pip install qiskit-mcp-servers[runtime] # Only Runtime pip install qiskit-mcp-servers[transpiler] # Only Transpiler pip install qiskit-mcp-servers[docs] # Only Docs @@ -61,7 +61,6 @@ Alternatively, you can create releases manually through the GitHub web interface 1. Go to PyPI and create the project (if it doesn't exist): - For `qiskit-mcp-server`: https://pypi.org/manage/project/qiskit-mcp-server/settings/publishing/ - - For `qiskit-code-assistant-mcp-server`: https://pypi.org/manage/project/qiskit-code-assistant-mcp-server/settings/publishing/ - For `qiskit-ibm-runtime-mcp-server`: https://pypi.org/manage/project/qiskit-ibm-runtime-mcp-server/settings/publishing/ - For `qiskit-ibm-transpiler-mcp-server`: https://pypi.org/manage/project/qiskit-ibm-transpiler-mcp-server/settings/publishing/ - For `qiskit-docs-mcp-server`: https://pypi.org/manage/project/qiskit-docs-mcp-server/settings/publishing/ @@ -69,7 +68,7 @@ Alternatively, you can create releases manually through the GitHub web interface - For `qiskit-mcp-servers`: https://pypi.org/manage/project/qiskit-mcp-servers/settings/publishing/ 2. Add a "trusted publisher" with these settings: - - **PyPI Project Name**: `qiskit-mcp-server` (or `qiskit-code-assistant-mcp-server`, `qiskit-ibm-runtime-mcp-server`, `qiskit-ibm-transpiler-mcp-server`, `qiskit-docs-mcp-server`, `qiskit-gym-mcp-server`, or `qiskit-mcp-servers`) + - **PyPI Project Name**: `qiskit-mcp-server` (or `qiskit-ibm-runtime-mcp-server`, `qiskit-ibm-transpiler-mcp-server`, `qiskit-docs-mcp-server`, `qiskit-gym-mcp-server`, or `qiskit-mcp-servers`) - **Owner**: `Qiskit` - **Repository**: `mcp-servers` - **Workflow name**: `publish-pypi.yml` @@ -84,7 +83,6 @@ The workflow automatically publishes when you create a GitHub release. The tag n | Tag Pattern | Package Published | |-------------|-------------------| | `qiskit-v*` | qiskit-mcp-server | -| `code-assistant-v*` | qiskit-code-assistant-mcp-server | | `runtime-v*` | qiskit-ibm-runtime-mcp-server | | `transpiler-v*` | qiskit-ibm-transpiler-mcp-server | | `docs-v*` | qiskit-docs-mcp-server | @@ -99,7 +97,6 @@ Follow these steps to release a package: Edit the version in the appropriate `pyproject.toml`: - **Qiskit**: `qiskit-mcp-server/pyproject.toml` -- **Code Assistant**: `qiskit-code-assistant-mcp-server/pyproject.toml` - **Runtime**: `qiskit-ibm-runtime-mcp-server/pyproject.toml` - **Transpiler**: `qiskit-ibm-transpiler-mcp-server/pyproject.toml` - **Docs**: `qiskit-docs-mcp-server/pyproject.toml` @@ -111,7 +108,7 @@ Edit the version in the appropriate `pyproject.toml`: ```bash # Stage and commit the version change git add -A -git commit -m "Bump qiskit-code-assistant-mcp-server to v0.1.1" +git commit -m "Bump qiskit-mcp-server to v0.1.1" # Push to main branch git push origin main @@ -121,18 +118,18 @@ git push origin main ```bash # Create an annotated tag -git tag -a code-assistant-v0.1.1 -m "Release qiskit-code-assistant-mcp-server v0.1.1" +git tag -a qiskit-v0.1.1 -m "Release qiskit-mcp-server v0.1.1" # Push the tag to GitHub -git push origin code-assistant-v0.1.1 +git push origin qiskit-v0.1.1 ``` ##### Step 4: Create GitHub Release ```bash # Create the release (this triggers the publish workflow) -gh release create code-assistant-v0.1.1 \ - --title "qiskit-code-assistant-mcp-server v0.1.1" \ +gh release create qiskit-v0.1.1 \ + --title "qiskit-mcp-server v0.1.1" \ --generate-notes ``` @@ -148,14 +145,6 @@ git tag -a qiskit-v0.1.1 -m "Release v0.1.1" && git push origin qiskit-v0.1.1 gh release create qiskit-v0.1.1 --title "qiskit-mcp-server v0.1.1" --generate-notes ``` -**Code Assistant Server:** -```bash -# After updating version in qiskit-code-assistant-mcp-server/pyproject.toml -git add -A && git commit -m "Bump code-assistant to v0.1.1" && git push origin main -git tag -a code-assistant-v0.1.1 -m "Release v0.1.1" && git push origin code-assistant-v0.1.1 -gh release create code-assistant-v0.1.1 --title "qiskit-code-assistant-mcp-server v0.1.1" --generate-notes -``` - **Runtime Server:** ```bash # After updating version in qiskit-ibm-runtime-mcp-server/pyproject.toml @@ -207,9 +196,6 @@ gh workflow run "Publish to PyPI" -f package=all # Publish only qiskit gh workflow run "Publish to PyPI" -f package=qiskit -# Publish only code-assistant -gh workflow run "Publish to PyPI" -f package=code-assistant - # Publish only runtime gh workflow run "Publish to PyPI" -f package=runtime @@ -230,7 +216,7 @@ Alternatively, you can trigger via the GitHub web interface: 1. Go to **Actions** → **Publish to PyPI** 2. Click **Run workflow** -3. Select which package to publish: `all`, `meta-package`, `qiskit`, `code-assistant`, `runtime`, `transpiler`, `docs`, or `gym` +3. Select which package to publish: `all`, `meta-package`, `qiskit`, `runtime`, `transpiler`, `docs`, or `gym` ## Manual Publishing @@ -254,7 +240,6 @@ pip install uv Edit the version in `pyproject.toml`: - **Qiskit**: `qiskit-mcp-server/pyproject.toml` -- **Code Assistant**: `qiskit-code-assistant-mcp-server/pyproject.toml` - **Runtime**: `qiskit-ibm-runtime-mcp-server/pyproject.toml` - **Transpiler**: `qiskit-ibm-transpiler-mcp-server/pyproject.toml` - **Docs**: `qiskit-docs-mcp-server/pyproject.toml` @@ -274,17 +259,6 @@ uv build python -m build ``` -**For Code Assistant:** -```bash -cd qiskit-code-assistant-mcp-server - -# Build with uv (recommended) -uv build - -# Or with build -python -m build -``` - **For Runtime:** ```bash cd qiskit-ibm-runtime-mcp-server @@ -361,8 +335,6 @@ twine upload --repository testpypi dist/* # Test installation pip install --index-url https://test.pypi.org/simple/ qiskit-mcp-server # or -pip install --index-url https://test.pypi.org/simple/ qiskit-code-assistant-mcp-server -# or pip install --index-url https://test.pypi.org/simple/ qiskit-ibm-runtime-mcp-server # or pip install --index-url https://test.pypi.org/simple/ qiskit-ibm-transpiler-mcp-server @@ -389,9 +361,6 @@ You'll be prompted for your PyPI username and password (or API token). # For Qiskit pip install qiskit-mcp-server -# For Code Assistant -pip install qiskit-code-assistant-mcp-server - # For Runtime pip install qiskit-ibm-runtime-mcp-server @@ -423,7 +392,6 @@ All packages use **semantic versioning**: `MAJOR.MINOR.PATCH` The current version for each package is defined in their respective `pyproject.toml` files: - **qiskit-mcp-server**: See [qiskit-mcp-server/pyproject.toml](qiskit-mcp-server/pyproject.toml) (search for `version =`) -- **qiskit-code-assistant-mcp-server**: See [qiskit-code-assistant-mcp-server/pyproject.toml](qiskit-code-assistant-mcp-server/pyproject.toml) (search for `version =`) - **qiskit-ibm-runtime-mcp-server**: See [qiskit-ibm-runtime-mcp-server/pyproject.toml](qiskit-ibm-runtime-mcp-server/pyproject.toml) (search for `version =`) - **qiskit-ibm-transpiler-mcp-server**: See [qiskit-ibm-transpiler-mcp-server/pyproject.toml](qiskit-ibm-transpiler-mcp-server/pyproject.toml) (search for `version =`) - **qiskit-docs-mcp-server**: See [qiskit-docs-mcp-server/pyproject.toml](qiskit-docs-mcp-server/pyproject.toml) (search for `version =`) @@ -470,7 +438,7 @@ password = pypi-YOUR-API-TOKEN-HERE Make sure you're running build commands from the package directory: ```bash -cd qiskit-mcp-server # or qiskit-code-assistant-mcp-server, qiskit-docs-mcp-server, qiskit-gym-mcp-server, etc. +cd qiskit-mcp-server # or qiskit-docs-mcp-server, qiskit-gym-mcp-server, etc. uv build ``` @@ -484,7 +452,7 @@ MCP Registry publishing is automated via GitHub Actions and triggers alongside P - **Workflow**: `.github/workflows/publish-mcp-registry.yml` - **Authentication**: GitHub OIDC (no secrets required) -- **Trigger**: Same release tags as PyPI (`qiskit-v*`, `code-assistant-v*`, etc.) +- **Trigger**: Same release tags as PyPI (`qiskit-v*`, `runtime-v*`, etc.) When you create a GitHub release, both workflows trigger automatically: 1. `publish-pypi.yml` → publishes to PyPI @@ -498,7 +466,6 @@ gh workflow run "Publish to MCP Registry" -f package=all # Publish specific server gh workflow run "Publish to MCP Registry" -f package=qiskit -gh workflow run "Publish to MCP Registry" -f package=code-assistant gh workflow run "Publish to MCP Registry" -f package=runtime gh workflow run "Publish to MCP Registry" -f package=transpiler gh workflow run "Publish to MCP Registry" -f package=docs diff --git a/README.md b/README.md index b28370c..4a524df 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ [![qiskit-mcp-server](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=qiskit-mcp-server&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-mcp-server) [![qiskit-ibm-runtime-mcp-server](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-ibm-runtime-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=qiskit-ibm-runtime-mcp-server&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-ibm-runtime-mcp-server) [![qiskit-ibm-transpiler-mcp-server](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-ibm-transpiler-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=qiskit-ibm-transpiler-mcp-server&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-ibm-transpiler-mcp-server) -[![qiskit-code-assistant-mcp-server](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-code-assistant-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=qiskit-code-assistant-mcp-server&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-code-assistant-mcp-server) [![qiskit-docs-mcp-server](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-docs-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=qiskit-docs-mcp-server&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-docs-mcp-server) [![qiskit-gym-mcp-server](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-gym-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=qiskit-gym-mcp-server&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-gym-mcp-server) @@ -20,7 +19,7 @@ A collection of [Model Context Protocol (MCP)](https://modelcontextprotocol.io/i ### Prerequisites - **Python 3.10+** (3.11+ recommended) -- **IBM Quantum account** and [API token](https://quantum.ibm.com) (only required for IBM cloud servers: Runtime, Transpiler, Code Assistant) +- **IBM Quantum account** and [API token](https://quantum.ibm.com) (only required for IBM cloud servers: Runtime, Transpiler) ### Install from PyPI @@ -37,7 +36,6 @@ pip install qiskit-mcp-servers ```bash pip install "qiskit-mcp-servers[qiskit]" # Qiskit server only -pip install "qiskit-mcp-servers[code-assistant]" # Code Assistant server only pip install "qiskit-mcp-servers[runtime]" # IBM Runtime server only pip install "qiskit-mcp-servers[transpiler]" # IBM Transpiler server only pip install "qiskit-mcp-servers[docs]" # Documentation server only @@ -79,7 +77,6 @@ claude mcp add qiskit-docs -- uvx qiskit-docs-mcp-server claude mcp add qiskit-gym -- uvx qiskit-gym-mcp-server # Require QISKIT_IBM_TOKEN (https://quantum.ibm.com) -claude mcp add qiskit-code-assistant -e QISKIT_IBM_TOKEN=$QISKIT_IBM_TOKEN -- uvx qiskit-code-assistant-mcp-server claude mcp add qiskit-ibm-runtime -e QISKIT_IBM_TOKEN=$QISKIT_IBM_TOKEN -- uvx qiskit-ibm-runtime-mcp-server claude mcp add qiskit-ibm-transpiler -e QISKIT_IBM_TOKEN=$QISKIT_IBM_TOKEN -- uvx qiskit-ibm-transpiler-mcp-server ``` @@ -111,15 +108,6 @@ Add to your `~/.bob/settings/mcp_settings.json`: "alwaysAllow": [], "disabled": false }, - "qiskit-code-assistant": { - "command": "uvx", - "args": ["qiskit-code-assistant-mcp-server"], - "env": { - "QISKIT_IBM_TOKEN": "" - }, - "alwaysAllow": [], - "disabled": false - }, "qiskit-ibm-runtime": { "command": "uvx", "args": ["qiskit-ibm-runtime-mcp-server"], @@ -163,13 +151,6 @@ Add to your `claude_desktop_config.json`: "command": "uvx", "args": ["qiskit-gym-mcp-server"] }, - "qiskit-code-assistant": { - "command": "uvx", - "args": ["qiskit-code-assistant-mcp-server"], - "env": { - "QISKIT_IBM_TOKEN": "your_ibm_quantum_token_here" - } - }, "qiskit-ibm-runtime": { "command": "uvx", "args": ["qiskit-ibm-runtime-mcp-server"], @@ -209,7 +190,6 @@ npx @modelcontextprotocol/inspector uvx qiskit-ibm-runtime-mcp-server | Server | Description | Directory | |--------|-------------|-----------| | **Qiskit MCP Server** | Circuit creation, transpilation, and serialization (QASM3, QPY) using [Qiskit](https://github.com/Qiskit/qiskit) | [`qiskit-mcp-server/`](./qiskit-mcp-server/) | -| **Qiskit Code Assistant** | AI-assisted quantum programming via [IBM Qiskit Code Assistant](https://quantum.cloud.ibm.com/docs/en/guides/qiskit-code-assistant) | [`qiskit-code-assistant-mcp-server/`](./qiskit-code-assistant-mcp-server/) | | **Qiskit IBM Runtime** | Full access to IBM Quantum hardware via [Qiskit IBM Runtime](https://github.com/Qiskit/qiskit-ibm-runtime/) | [`qiskit-ibm-runtime-mcp-server/`](./qiskit-ibm-runtime-mcp-server/) | | **Qiskit IBM Transpiler** | AI-optimized circuit routing and optimization via [qiskit-ibm-transpiler](https://github.com/Qiskit/qiskit-ibm-transpiler) | [`qiskit-ibm-transpiler-mcp-server/`](./qiskit-ibm-transpiler-mcp-server/) | | **Qiskit Docs** | Search and retrieve [Qiskit documentation](https://quantum.cloud.ibm.com/docs/), guides, and API references. No auth required. | [`qiskit-docs-mcp-server/`](./qiskit-docs-mcp-server/) | @@ -220,6 +200,10 @@ npx @modelcontextprotocol/inspector uvx qiskit-ibm-runtime-mcp-server |--------|-------------|-----------| | **Qiskit Gym** | RL-based quantum circuit synthesis using [qiskit-gym](https://github.com/rl-institut/qiskit-gym) (permutation routing, linear functions, Clifford circuits) | [`qiskit-gym-mcp-server/`](./qiskit-gym-mcp-server/) | +### Removed Servers + +- **Qiskit Code Assistant MCP Server** — previously published as `qiskit-code-assistant-mcp-server`. Removed because the underlying Qiskit Code Assistant service has been discontinued by IBM Quantum. See the [sunset announcement](https://quantum.cloud.ibm.com/announcements/en/product-updates/2026-04-28-qiskit-code-assistant-service-to-sunset). The PyPI package is archived and no longer maintained. + ## Examples Each server includes an `examples/` directory with a **Jupyter notebook** (`langchain_agent.ipynb`) and a **Python script** (`langchain_agent.py`) showing how to build AI agents with LangChain. @@ -240,7 +224,7 @@ All servers are built on [FastMCP](https://github.com/jlowin/fastmcp) and share ### Running Tests ```bash -cd qiskit-code-assistant-mcp-server # or any server directory +cd qiskit-mcp-server # or any server directory ./run_tests.sh ``` @@ -254,7 +238,6 @@ cd qiskit-code-assistant-mcp-server # or any server directory - [Model Context Protocol](https://modelcontextprotocol.io/introduction) — understanding MCP - [Qiskit IBM Runtime docs](https://quantum.cloud.ibm.com/docs/en/api/qiskit-ibm-runtime) — quantum cloud services -- [Qiskit Code Assistant docs](https://quantum.cloud.ibm.com/docs/en/guides/qiskit-code-assistant) — AI code assistance - [MCP Inspector](https://github.com/modelcontextprotocol/inspector) — interactive testing tool - [FastMCP](https://github.com/jlowin/fastmcp) — high-performance MCP framework - [AGENTS.md](AGENTS.md) — guidance for AI coding assistants (IBM Bob, Claude Code, Copilot, Cursor, and others) diff --git a/examples/README.md b/examples/README.md index e88273e..0f99b2d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -252,5 +252,4 @@ Each MCP server has simpler examples in its own directory: - [`qiskit-ibm-runtime-mcp-server/examples/`](../qiskit-ibm-runtime-mcp-server/examples/) - IBM Quantum Runtime - [`qiskit-ibm-transpiler-mcp-server/examples/`](../qiskit-ibm-transpiler-mcp-server/examples/) - AI transpilation - [`qiskit-docs-mcp-server/examples/`](../qiskit-docs-mcp-server/examples/) - Documentation retrieval -- [`qiskit-code-assistant-mcp-server/examples/`](../qiskit-code-assistant-mcp-server/examples/) - Code generation - [`qiskit-gym-mcp-server/examples/`](../qiskit-gym-mcp-server/examples/) - RL-based circuit synthesis (community) diff --git a/mypy.ini b/mypy.ini index 89041b2..29694f3 100644 --- a/mypy.ini +++ b/mypy.ini @@ -48,7 +48,7 @@ incremental = True cache_dir = .mypy_cache # Paths to check -files = qiskit-code-assistant-mcp-server/src,qiskit-ibm-runtime-mcp-server/src,qiskit-ibm-transpiler-mcp-server/src,qiskit-mcp-server/src,qiskit-gym-mcp-server/src +files = qiskit-ibm-runtime-mcp-server/src,qiskit-ibm-transpiler-mcp-server/src,qiskit-mcp-server/src,qiskit-gym-mcp-server/src # Exclude patterns exclude = (^\.venv/|^build/|^dist/|/tests/) @@ -66,9 +66,6 @@ ignore_missing_imports = True [mypy-httpx.*] ignore_missing_imports = True -[mypy-respx.*] -ignore_missing_imports = True - [mypy-pytest.*] ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index 3d889b4..2db5066 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qiskit-mcp-servers" -version = "0.10.0" +version = "0.11.0" description = "Model Context Protocol servers for IBM Quantum services and Qiskit" readme = "README.md" requires-python = ">=3.10,<3.15" @@ -26,7 +26,6 @@ classifiers = [ # Meta-package dependencies: installs all MCP servers dependencies = [ "qiskit-mcp-server>=0.2.0", - "qiskit-code-assistant-mcp-server>=0.3.0", "qiskit-docs-mcp-server>=0.1.1", "qiskit-ibm-runtime-mcp-server>=0.5.0", "qiskit-ibm-transpiler-mcp-server>=0.3.1", @@ -35,7 +34,6 @@ dependencies = [ [project.optional-dependencies] # Install individual servers qiskit = ["qiskit-mcp-server>=0.2.0"] -code-assistant = ["qiskit-code-assistant-mcp-server>=0.3.0"] docs = ["qiskit-docs-mcp-server>=0.1.1"] runtime = ["qiskit-ibm-runtime-mcp-server>=0.5.0"] transpiler = ["qiskit-ibm-transpiler-mcp-server>=0.3.1"] @@ -43,7 +41,6 @@ gym = ["qiskit-gym-mcp-server>=0.3.0"] # Install all servers (core + community) all = [ "qiskit-mcp-server>=0.2.0", - "qiskit-code-assistant-mcp-server>=0.3.0", "qiskit-docs-mcp-server>=0.1.1", "qiskit-ibm-runtime-mcp-server>=0.5.0", "qiskit-ibm-transpiler-mcp-server>=0.3.1", @@ -72,7 +69,6 @@ packages = ["src/qiskit_mcp_servers"] [tool.uv.workspace] members = [ "qiskit-mcp-server", - "qiskit-code-assistant-mcp-server", "qiskit-ibm-runtime-mcp-server", "qiskit-ibm-transpiler-mcp-server", "qiskit-gym-mcp-server", @@ -82,7 +78,6 @@ members = [ # Source configuration for workspace dependencies [tool.uv.sources] qiskit-mcp-server = { workspace = true } -qiskit-code-assistant-mcp-server = { workspace = true } qiskit-ibm-runtime-mcp-server = { workspace = true } qiskit-ibm-transpiler-mcp-server = { workspace = true } qiskit-gym-mcp-server = { workspace = true } @@ -98,7 +93,7 @@ extend = "ruff.toml" # Note: mypy doesn't support extend in pyproject.toml, see mypy.ini [tool.pytest.ini_options] -testpaths = ["qiskit-mcp-server/tests", "qiskit-code-assistant-mcp-server/tests", "qiskit-docs-mcp-server/tests", "qiskit-ibm-runtime-mcp-server/tests", "qiskit-ibm-transpiler-mcp-server/tests", "qiskit-gym-mcp-server/tests"] +testpaths = ["qiskit-mcp-server/tests", "qiskit-docs-mcp-server/tests", "qiskit-ibm-runtime-mcp-server/tests", "qiskit-ibm-transpiler-mcp-server/tests", "qiskit-gym-mcp-server/tests"] python_files = "test_*.py" python_classes = "Test*" python_functions = "test_*" @@ -121,7 +116,6 @@ markers = [ [tool.coverage.run] source = [ "qiskit-mcp-server/src", - "qiskit-code-assistant-mcp-server/src", "qiskit-docs-mcp-server/src", "qiskit-ibm-runtime-mcp-server/src", "qiskit-ibm-transpiler-mcp-server/src", diff --git a/qiskit-code-assistant-mcp-server/.env.example b/qiskit-code-assistant-mcp-server/.env.example deleted file mode 100644 index d2b54f3..0000000 --- a/qiskit-code-assistant-mcp-server/.env.example +++ /dev/null @@ -1,19 +0,0 @@ -# Qiskit Code Assistant MCP Server Configuration - -# IBM Quantum API Token (required) -# Get your token from: https://quantum.ibm.com/account -QISKIT_IBM_TOKEN=your_ibm_quantum_token_here - -# Qiskit Code Assistant API Configuration -QCA_TOOL_API_BASE=https://qiskit-code-assistant.quantum.ibm.com - -# Default model name -# Note: The server automatically checks model availability at startup. -# If this model is unavailable, it will automatically use the first available model. -QCA_TOOL_MODEL_NAME=mistral-small-3.2-24b-qiskit - -# Request timeout in seconds -QCA_REQUEST_TIMEOUT=30.0 - -# Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) -QCA_MCP_DEBUG_LEVEL=INFO \ No newline at end of file diff --git a/qiskit-code-assistant-mcp-server/LICENSE b/qiskit-code-assistant-mcp-server/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/qiskit-code-assistant-mcp-server/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/qiskit-code-assistant-mcp-server/README.md b/qiskit-code-assistant-mcp-server/README.md deleted file mode 100644 index 3061112..0000000 --- a/qiskit-code-assistant-mcp-server/README.md +++ /dev/null @@ -1,256 +0,0 @@ -# qiskit-code-assistant-mcp-server - -[![MCP Registry](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0.1%2Fservers%2Fio.github.Qiskit%252Fqiskit-code-assistant-mcp-server%2Fversions%2Flatest&query=%24.server.version&label=MCP%20Registry&logo=modelcontextprotocol)](https://registry.modelcontextprotocol.io/?q=io.github.Qiskit%2Fqiskit-code-assistant-mcp-server) - - - -MCP server for Qiskit Code Assistant - - -## Components - - -### Tools - -The server implements four tools: -- `accept_model_disclaimer_tool`: Accept the disclaimer for a given model -- `get_completion_tool`: Get completion for a given prompt -- `get_rag_completion_tool`: Get RAG completion for answering conceptual or descriptive questions about Qiskit or Quantum -- `accept_completion_tool`: Accept a given completion - - -## Prerequisites - -- Python 3.10 or higher -- [uv](https://astral.sh/uv) package manager (recommended) -- IBM Quantum account and API token -- Access to Qiskit Code Assistant service - -## Installation - -### Install from PyPI - -The easiest way to install is via pip: - -```bash -pip install qiskit-code-assistant-mcp-server -``` - -### Install from Source - -This project uses [uv](https://astral.sh/uv) for virtual environments and dependencies management. If you don't have `uv` installed, check out the instructions in - -### Setting up the Project with uv - -1. **Initialize or sync the project**: - ```bash - # This will create a virtual environment and install dependencies - uv sync - ``` - -2. **Configure environment variables**: - ```bash - # Copy the example environment file - cp .env.example .env - - # Edit .env and add your IBM Quantum API token - # Get your token from: https://cloud.quantum.ibm.com/ - ``` - -## Configuration - -### Environment Variables - -The server can be configured using environment variables in your `.env` file: - -- `QISKIT_IBM_TOKEN` - Your IBM Quantum API token (required) -- `QCA_TOOL_API_BASE` - Qiskit Code Assistant API base URL (default: `https://qiskit-code-assistant.quantum.ibm.com`) -- `QCA_TOOL_MODEL_NAME` - Default model name (default: `mistral-small-3.2-24b-qiskit`) -- `QCA_REQUEST_TIMEOUT` - Request timeout in seconds (default: `30.0`) -- `QCA_MCP_DEBUG_LEVEL` - Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL (default: `INFO`) - -### Model Selection - -The server includes an **automatic model availability guardrail** that: -- Checks available models from the Qiskit Code Assistant service at startup -- Uses the configured `QCA_TOOL_MODEL_NAME` if available -- Automatically falls back to the first available model if the default is unavailable -- Logs warnings when using a fallback model -- Gracefully handles API errors by using the configured default - -This ensures the server can start and function even when the default model is temporarily unavailable. - -## Quick Start - -### Running the Server - -```bash -uv run qiskit-code-assistant-mcp-server -``` - -The server will start and listen for MCP connections. - -### Synchronous Usage - -For frameworks that don't support async operations, all async functions have a `.sync` attribute for synchronous execution: - -```python -from qiskit_code_assistant_mcp_server.qca import ( - get_completion, - get_rag_completion, - list_models -) - -# Use .sync for synchronous execution -result = get_completion.sync("Write a quantum circuit for a Bell state") -print(result) - -# Works in Jupyter notebooks (handles nested event loops automatically) -rag_result = get_rag_completion.sync("What is quantum entanglement?") -print(rag_result) - -# List available models -models = list_models.sync() -print(models) -``` - -**Available functions (all support `.sync`):** -- `list_models()` - List available models -- `get_model(model_id)` - Get model info -- `get_completion(prompt)` - Get code completion -- `get_rag_completion(prompt)` - Get RAG-based completion -- `accept_completion(completion_id)` - Accept a completion -- `get_service_status()` - Get service status - -**LangChain Integration Example:** - -> **Note:** To run LangChain examples you will need to install the dependencies: -> ```bash -> pip install langchain langchain-mcp-adapters langchain-openai python-dotenv -> ``` - -```python -import asyncio -import os -from langchain.agents import create_agent -from langchain_mcp_adapters.client import MultiServerMCPClient -from langchain_mcp_adapters.tools import load_mcp_tools -from langchain_openai import ChatOpenAI -from dotenv import load_dotenv - -# Load environment variables (QISKIT_IBM_TOKEN, OPENAI_API_KEY, etc.) -load_dotenv() - -async def main(): - # Configure MCP client - mcp_client = MultiServerMCPClient({ - "qiskit-code-assistant": { - "transport": "stdio", - "command": "qiskit-code-assistant-mcp-server", - "args": [], - "env": { - "QISKIT_IBM_TOKEN": os.getenv("QISKIT_IBM_TOKEN", ""), - }, - } - }) - - # Use persistent session for efficient tool calls - async with mcp_client.session("qiskit-code-assistant") as session: - tools = await load_mcp_tools(session) - - # Create agent with LLM - llm = ChatOpenAI(model="gpt-5.2", temperature=0) - agent = create_agent(llm, tools) - - # Run a query - response = await agent.ainvoke("Write a quantum circuit that creates a Bell state") - print(response) - -asyncio.run(main()) -``` - -For more LLM providers (Anthropic, Google, Ollama, Watsonx) and detailed examples including Jupyter notebooks, see the [examples/](examples/) directory. - - -### Testing and debugging the server - -> _**Note**: to launch the MCP inspector you will need to have [`node` and `npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)_ - -1. From a terminal, go into the cloned repo directory - -1. Switch to the virtual environment - - ```sh - source .venv/bin/activate - ``` - -1. Run the MCP Inspector: - - ```sh - npx @modelcontextprotocol/inspector uv run qiskit-code-assistant-mcp-server - ``` - -1. Open your browser to the URL shown in the console message e.g., - - ``` - MCP Inspector is up and running at http://localhost:5173 - ``` - -## Testing - -This project includes comprehensive unit and integration tests. - -### Running Tests - -**Quick test run:** -```bash -./run_tests.sh -``` - -**Manual test commands:** -```bash -# Install test dependencies -uv sync --group dev --group test - -# Run all tests -uv run pytest - -# Run only unit tests -uv run pytest -m "not integration" - -# Run only integration tests -uv run pytest -m "integration" - -# Run tests with coverage -uv run pytest --cov=src --cov-report=html - -# Run specific test file -uv run pytest tests/test_qca.py -v -``` - -### Test Structure - -- `tests/test_qca.py` - Unit tests for QCA functions -- `tests/test_utils.py` - Unit tests for utility functions -- `tests/test_constants.py` - Unit tests for configuration -- `tests/test_sync.py` - Unit tests for synchronous execution -- `tests/test_integration.py` - Integration tests -- `tests/conftest.py` - Test fixtures and configuration - -### Test Coverage - -The test suite covers: -- ✅ All QCA API interactions -- ✅ Model selection and availability guardrail -- ✅ Error handling and validation -- ✅ HTTP client management -- ✅ Synchronous execution (`.sync` methods) -- ✅ Configuration validation -- ✅ Integration scenarios -- ✅ Resource and tool handlers - -## Resources - -- [Qiskit Code Assistant](https://docs.quantum.ibm.com/guides/qiskit-code-assistant) -- [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) -- [MCP Inspector](https://github.com/modelcontextprotocol/inspector) diff --git a/qiskit-code-assistant-mcp-server/examples/README.md b/qiskit-code-assistant-mcp-server/examples/README.md deleted file mode 100644 index 5ed7829..0000000 --- a/qiskit-code-assistant-mcp-server/examples/README.md +++ /dev/null @@ -1,280 +0,0 @@ -# Qiskit Code Assistant MCP Server Examples - -This directory contains examples demonstrating how to build AI agents that interact with Qiskit Code Assistant through the **qiskit-code-assistant-mcp-server**. - -## Available Examples - -| File | Description | -|------|-------------| -| [`langchain_agent.ipynb`](langchain_agent.ipynb) | **Jupyter Notebook** - Interactive tutorial with step-by-step examples | -| [`langchain_agent.py`](langchain_agent.py) | **Python Script** - Command-line agent with multiple LLM provider support | - -## LangChain Agent Example - -The examples show how to create an AI agent using LangChain that connects to the qiskit-code-assistant-mcp-server via the Model Context Protocol (MCP). - -### Quick Start with Jupyter Notebook - -For an interactive experience, open the notebook: - -```bash -jupyter notebook langchain_agent.ipynb -``` - -The notebook includes: -- Step-by-step setup instructions -- Multiple LLM provider options (just run the cell for your provider) -- Interactive examples for generating quantum code and asking questions -- A custom query cell for your own questions - -### Features - -The agent can: - -- Generate quantum code completions using Qiskit Code Assistant -- Answer questions about Qiskit and quantum computing concepts (RAG mode) -- Accept model disclaimers when required -- Track completion acceptance for feedback - -### Supported LLM Providers - -| Provider | Package | Default Model | API Key Required | -|----------|---------|---------------|------------------| -| OpenAI | `langchain-openai` | gpt-5.2 | Yes (`OPENAI_API_KEY`) | -| Anthropic | `langchain-anthropic` | claude-sonnet-4-5-20250929 | Yes (`ANTHROPIC_API_KEY`) | -| Google | `langchain-google-genai` | gemini-3-pro-preview | Yes (`GOOGLE_API_KEY`) | -| Ollama | `langchain-ollama` | llama3.3 | No (runs locally) | -| Watsonx | `langchain-ibm` | ibm/granite-4-h-small | Yes (`WATSONX_APIKEY`, `WATSONX_PROJECT_ID`) | - -### Architecture - -``` -┌─────────────┐ MCP Protocol ┌──────────────────────────────────┐ -│ LangChain │ ◄──────────────────► │ qiskit-code-assistant-mcp-server │ -│ Agent │ │ │ -└─────────────┘ │ ┌────────────────────────────┐ │ - │ │ Qiskit Code Assistant API │ │ - │ └────────────────────────────┘ │ - │ │ │ - └───────────────│──────────────────┘ - ▼ - ┌─────────────────┐ - │ IBM Quantum │ - │ Code Assistant │ - └─────────────────┘ -``` - -### Prerequisites - -1. **Python 3.10+** - -2. **Install the MCP server:** - -```bash -pip install qiskit-code-assistant-mcp-server -``` - -3. **Install LangChain dependencies:** - -```bash -# Core dependencies -pip install langchain langchain-mcp-adapters python-dotenv - -# Install at least ONE of the following based on your preferred LLM provider(s): -pip install langchain-openai # For OpenAI -pip install langchain-anthropic # For Anthropic Claude -pip install langchain-google-genai # For Google Gemini -pip install langchain-ollama # For local Ollama -pip install langchain-ibm # For IBM Watsonx -``` - -4. **Set up environment variables:** - -```bash -# IBM Quantum token (required) -export QISKIT_IBM_TOKEN="your-ibm-quantum-token" - -# LLM API key (depends on provider) -export OPENAI_API_KEY="your-openai-api-key" # For OpenAI -export ANTHROPIC_API_KEY="your-anthropic-api-key" # For Anthropic -export GOOGLE_API_KEY="your-google-api-key" # For Google -# No API key needed for Ollama (runs locally) - -# For Watsonx -export WATSONX_APIKEY="your-watsonx-api-key" -export WATSONX_PROJECT_ID="your-project-id" -export WATSONX_URL="https://us-south.ml.cloud.ibm.com" # Optional, this is the default -``` - -Or create a `.env` file: - -```env -QISKIT_IBM_TOKEN=your-ibm-quantum-token -OPENAI_API_KEY=your-openai-api-key - -# For Watsonx -WATSONX_APIKEY=your-watsonx-api-key -WATSONX_PROJECT_ID=your-project-id -``` - -### Getting Your IBM Quantum Token - -1. Create an account at [IBM Quantum](https://quantum.ibm.com/) -2. Navigate to your account settings -3. Copy your API token - -### Running the Example - -**Interactive mode with OpenAI (default):** - -```bash -cd examples -python langchain_agent.py -``` - -**With Anthropic Claude:** - -```bash -python langchain_agent.py --provider anthropic -``` - -**With Google Gemini:** - -```bash -python langchain_agent.py --provider google -``` - -**With local Ollama (no API key needed):** - -```bash -# First, make sure Ollama is running with a model pulled -# ollama pull llama3.3 -python langchain_agent.py --provider ollama --model llama3.3 -``` - -**With IBM Watsonx:** - -```bash -python langchain_agent.py --provider watsonx -# Or with a specific model -python langchain_agent.py --provider watsonx --model ibm/granite-4-h-small -``` - -**Single query mode:** - -```bash -python langchain_agent.py --single -python langchain_agent.py --provider anthropic --single -``` - -**Custom model:** - -```bash -python langchain_agent.py --provider openai --model gpt-4-turbo -python langchain_agent.py --provider anthropic --model claude-3-haiku-20240307 -``` - -### Example Interactions - -Once running, you can ask the agent questions like: - -- "Write a quantum circuit that creates a Bell state" -- "What is quantum entanglement and why is it important?" -- "Generate code to set up a simple VQE algorithm" -- "How does Qiskit's transpiler work?" -- "Write a circuit with 3 qubits that applies a Toffoli gate" - -### Available MCP Tools - -The agent has access to these tools provided by the MCP server: - -| Tool | Description | -|------|-------------| -| `get_completion_tool` | Generate Qiskit code completions based on prompts | -| `get_rag_completion_tool` | Answer questions about Qiskit and quantum computing using RAG | -| `accept_model_disclaimer_tool` | Accept the disclaimer for a model | -| `accept_completion_tool` | Accept/acknowledge a generated completion | - -### Using as a Library - -You can import and use the agent in your own async code: - -```python -import asyncio -from langchain_agent import ( - get_mcp_client, - create_quantum_agent_with_session, - run_agent_query, -) - -async def main(): - # Use persistent session for efficient tool calls - mcp_client = get_mcp_client() - async with mcp_client.session("qiskit-code-assistant") as session: - agent = await create_quantum_agent_with_session(session, provider="openai") - - # Run queries - response = await run_agent_query(agent, "Write a Bell state circuit") - print(response) - -asyncio.run(main()) -``` - -### Customizing the Agent - -You can modify the system prompt or use a different LLM by creating your own agent setup: - -```python -import asyncio -import os -from langchain.agents import create_agent -from langchain_mcp_adapters.client import MultiServerMCPClient -from langchain_mcp_adapters.tools import load_mcp_tools -from langchain_openai import ChatOpenAI - -async def create_custom_agent(): - # Configure MCP client - mcp_client = MultiServerMCPClient({ - "qiskit-code-assistant": { - "transport": "stdio", - "command": "qiskit-code-assistant-mcp-server", - "args": [], - "env": { - "QISKIT_IBM_TOKEN": os.getenv("QISKIT_IBM_TOKEN", ""), - }, - } - }) - - # Use persistent session for efficient tool calls - async with mcp_client.session("qiskit-code-assistant") as session: - tools = await load_mcp_tools(session) - - # Custom system prompt - system_prompt = "You are a quantum computing expert assistant..." - - llm = ChatOpenAI(model="gpt-5.2", temperature=0) - agent = create_agent(llm, tools, system_prompt=system_prompt) - - # Use the agent within the session context - # ... your agent logic here ... - -asyncio.run(create_custom_agent()) -``` - -### Troubleshooting - -**"MCP server not found"** -- Ensure `qiskit-code-assistant-mcp-server` is installed and available in your PATH -- Try running `qiskit-code-assistant-mcp-server` directly to verify installation - -**"Authentication failed"** -- Verify your `QISKIT_IBM_TOKEN` is correct -- Check that your IBM Quantum account has access to Qiskit Code Assistant - -**"Connection timeout"** -- The MCP server may take a few seconds to start -- Check your network connection to IBM Quantum services - -**"Model disclaimer required"** -- The agent will automatically use `accept_model_disclaimer_tool` when needed -- You can also manually accept the disclaimer through the tool diff --git a/qiskit-code-assistant-mcp-server/examples/langchain_agent.ipynb b/qiskit-code-assistant-mcp-server/examples/langchain_agent.ipynb deleted file mode 100644 index e9e7a41..0000000 --- a/qiskit-code-assistant-mcp-server/examples/langchain_agent.ipynb +++ /dev/null @@ -1,854 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# LangChain Agent with Qiskit Code Assistant MCP Server\n", - "\n", - "This notebook demonstrates how to create an AI agent using LangChain that connects to the **qiskit-code-assistant-mcp-server** via the Model Context Protocol (MCP).\n", - "\n", - "The agent can interact with Qiskit Code Assistant to:\n", - "- Generate quantum code completions\n", - "- Answer questions about Qiskit and quantum computing (RAG mode)\n", - "- Accept model disclaimers\n", - "- Track completion acceptance\n", - "\n", - "## Architecture\n", - "\n", - "```\n", - "┌─────────────┐ MCP Protocol ┌──────────────────────────────────┐\n", - "│ LangChain │ ◄──────────────────► │ qiskit-code-assistant-mcp-server │\n", - "│ Agent │ │ │\n", - "└─────────────┘ │ ┌────────────────────────────┐ │\n", - " │ │ Qiskit Code Assistant API │ │\n", - " │ └────────────────────────────┘ │\n", - " │ │ │\n", - " └───────────────│──────────────────┘\n", - " ▼\n", - " ┌─────────────────┐\n", - " │ IBM Quantum │\n", - " │ Code Assistant │\n", - " └─────────────────┘\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "### 1. Install Dependencies\n", - "\n", - "Run these commands in your terminal:\n", - "\n", - "```bash\n", - "# Install the MCP server with examples dependencies\n", - "pip install qiskit-code-assistant-mcp-server[examples]\n", - "\n", - "# Install your preferred LLM provider (choose one):\n", - "pip install langchain-openai # For OpenAI\n", - "pip install langchain-anthropic # For Anthropic Claude\n", - "pip install langchain-google-genai # For Google Gemini\n", - "pip install langchain-ollama # For local Ollama\n", - "pip install langchain-ibm # For IBM Watsonx\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2. Configure Environment Variables\n", - "\n", - "Set your IBM Quantum token for the Qiskit Code Assistant.\n", - "\n", - "You can either:\n", - "- Set them in a `.env` file in this directory\n", - "- Set them as environment variables\n", - "- Enter them in the cell below" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration status:\n", - " QISKIT_IBM_TOKEN: ✓ Set\n" - ] - } - ], - "source": [ - "import os\n", - "\n", - "from dotenv import load_dotenv\n", - "\n", - "# LangChain imports\n", - "from langchain.agents import create_agent\n", - "from langchain_core.messages import HumanMessage\n", - "from langchain_mcp_adapters.client import MultiServerMCPClient\n", - "from langchain_mcp_adapters.tools import load_mcp_tools\n", - "\n", - "\n", - "# Load from .env file if it exists\n", - "load_dotenv()\n", - "\n", - "# Or set directly (uncomment and fill in):\n", - "# os.environ[\"QISKIT_IBM_TOKEN\"] = \"your-ibm-quantum-token\"\n", - "\n", - "# Set your LLM provider API key (uncomment the one you're using):\n", - "# os.environ[\"OPENAI_API_KEY\"] = \"your-openai-api-key\"\n", - "# os.environ[\"ANTHROPIC_API_KEY\"] = \"your-anthropic-api-key\"\n", - "# os.environ[\"GOOGLE_API_KEY\"] = \"your-google-api-key\"\n", - "\n", - "# Verify configuration\n", - "print(\"Configuration status:\")\n", - "print(f\" QISKIT_IBM_TOKEN: {'✓ Set' if os.getenv('QISKIT_IBM_TOKEN') else '✗ Not set'}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Choose Your LLM Provider\n", - "\n", - "Run **one** of the following cells based on your preferred LLM provider:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Option 1: OpenAI\n", - "from langchain_openai import ChatOpenAI\n", - "\n", - "\n", - "llm = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", - "print(\"Using OpenAI GPT-4o\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Using Anthropic Claude Sonnet\n" - ] - } - ], - "source": [ - "# Option 2: Anthropic Claude\n", - "from langchain_anthropic import ChatAnthropic\n", - "\n", - "\n", - "llm = ChatAnthropic(model=\"claude-sonnet-4-20250514\", temperature=0)\n", - "print(\"Using Anthropic Claude Sonnet\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Option 3: Google Gemini\n", - "from langchain_google_genai import ChatGoogleGenerativeAI\n", - "\n", - "\n", - "llm = ChatGoogleGenerativeAI(model=\"gemini-2.5-pro\", temperature=0)\n", - "print(\"Using Google Gemini Pro\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Option 4: Local Ollama (no API key needed)\n", - "from langchain_ollama import ChatOllama\n", - "\n", - "\n", - "llm = ChatOllama(model=\"llama3.2\", temperature=0)\n", - "print(\"Using local Ollama with Llama 3.2\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Option 5: IBM Watsonx\n", - "from langchain_ibm import ChatWatsonx\n", - "\n", - "\n", - "llm = ChatWatsonx(\n", - " model_id=\"ibm/granite-3-8b-instruct\",\n", - " url=os.getenv(\"WATSONX_URL\", \"https://us-south.ml.cloud.ibm.com\"),\n", - " project_id=os.getenv(\"WATSONX_PROJECT_ID\"),\n", - " params={\"temperature\": 0, \"max_tokens\": 4096},\n", - ")\n", - "print(\"Using IBM Watsonx Granite\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define the System Prompt\n", - "\n", - "This prompt tells the agent what it can do and how to behave:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "SYSTEM_PROMPT = \"\"\"You are a helpful quantum computing coding assistant with access to Qiskit Code Assistant\n", - "through the MCP server.\n", - "\n", - "You can help users:\n", - "- Generate quantum code using Qiskit (get_completion_tool)\n", - "- Answer questions about Qiskit and quantum computing concepts (get_rag_completion_tool)\n", - "- Accept model disclaimers when required (accept_model_disclaimer_tool)\n", - "- Track completion acceptance for feedback (accept_completion_tool)\n", - "\n", - "When generating code:\n", - "- Use the get_completion_tool to generate Qiskit code based on user prompts\n", - "- Provide clear explanations of the generated code\n", - "- Suggest improvements or alternatives when appropriate\n", - "\n", - "When answering questions:\n", - "- Use the get_rag_completion_tool for conceptual questions about quantum computing\n", - "- Provide accurate and educational explanations\n", - "- Reference Qiskit documentation when relevant\n", - "\n", - "Always provide clear explanations about quantum computing concepts when relevant.\n", - "If an operation fails, explain the error and suggest possible solutions.\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create the MCP Client\n", - "\n", - "This configures the connection to the qiskit-code-assistant-mcp-server:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def get_mcp_client():\n", - " \"\"\"Create and return an MCP client configured for the Qiskit Code Assistant server.\"\"\"\n", - " return MultiServerMCPClient(\n", - " {\n", - " \"qiskit-code-assistant\": {\n", - " \"transport\": \"stdio\",\n", - " \"command\": \"qiskit-code-assistant-mcp-server\",\n", - " \"args\": [],\n", - " \"env\": {\n", - " \"QISKIT_IBM_TOKEN\": os.getenv(\"QISKIT_IBM_TOKEN\", \"\"),\n", - " },\n", - " }\n", - " }\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create the Agent\n", - "\n", - "Now we'll create a function that sets up the agent with a persistent MCP session.\n", - "\n", - "Using a persistent session is important because it:\n", - "- Keeps a single MCP server process running\n", - "- Reuses the Qiskit Code Assistant connection\n", - "- Makes tool calls much faster" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "async def create_agent_with_session(session):\n", - " \"\"\"Create a LangChain agent using an existing MCP session.\"\"\"\n", - " # Load tools from the existing session\n", - " tools = await load_mcp_tools(session)\n", - " print(f\"Loaded {len(tools)} tools from MCP server:\")\n", - " for tool in tools:\n", - " print(f\" - {tool.name}\")\n", - "\n", - " # Create the agent using LangChain's create_agent\n", - " agent = create_agent(llm, tools, system_prompt=SYSTEM_PROMPT)\n", - " return agent" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Helper Function to Run Queries\n", - "\n", - "This function sends a query to the agent and returns the response:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "async def ask_agent(agent, query: str) -> str:\n", - " \"\"\"Send a query to the agent and return the response.\"\"\"\n", - " result = await agent.ainvoke({\"messages\": [HumanMessage(content=query)]})\n", - " messages = result.get(\"messages\", [])\n", - " if messages:\n", - " return messages[-1].content\n", - " return \"No response generated.\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run the Agent\n", - "\n", - "Now let's create the agent and ask it some questions!\n", - "\n", - "The following cell starts the MCP server, creates the agent, and keeps the session open for multiple queries:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Starting MCP server and creating agent...\n", - "(This may take a few seconds on first run)\n", - "\n" - ] - } - ], - "source": [ - "# Create MCP client and start a persistent session\n", - "mcp_client = get_mcp_client()\n", - "\n", - "print(\"Starting MCP server and creating agent...\")\n", - "print(\"(This may take a few seconds on first run)\\n\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 1: Generate a Bell State Circuit\n", - "\n", - "Let's ask the agent to generate code for a Bell state:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 4 tools from MCP server:\n", - " - accept_model_disclaimer_tool\n", - " - get_completion_tool\n", - " - get_rag_completion_tool\n", - " - accept_completion_tool\n", - "Perfect! Here's a quantum circuit that creates a **Bell state**:\n", - "\n", - "```python\n", - "from qiskit import QuantumCircuit\n", - "\n", - "# Create a Bell state circuit\n", - "bell = QuantumCircuit(2)\n", - "bell.h(0)\n", - "bell.cx(0, 1)\n", - "\n", - "# Draw the circuit\n", - "print(bell.draw())\n", - "```\n", - "\n", - "## Explanation:\n", - "\n", - "A **Bell state** is a maximally entangled two-qubit quantum state. Here's what the circuit does:\n", - "\n", - "1. **`bell.h(0)`** - Applies a Hadamard gate to qubit 0, putting it into a superposition state: (|0⟩ + |1⟩)/√2\n", - "\n", - "2. **`bell.cx(0, 1)`** - Applies a CNOT (Controlled-NOT) gate with qubit 0 as the control and qubit 1 as the target. This creates entanglement.\n", - "\n", - "The resulting state is: **|Φ⁺⟩ = (|00⟩ + |11⟩)/√2**\n", - "\n", - "This is one of the four Bell states, and it's a maximally entangled state where measuring one qubit instantly determines the state of the other qubit.\n", - "\n", - "The circuit is very simple but demonstrates one of the most fundamental quantum phenomena - **quantum entanglement**! Would you like me to show you how to measure this circuit, create other Bell states, or explain more about quantum entanglement?\n" - ] - } - ], - "source": [ - "async with mcp_client.session(\"qiskit-code-assistant\") as session:\n", - " agent = await create_agent_with_session(session)\n", - "\n", - " response = await ask_agent(agent, \"Write a quantum circuit that creates a Bell state\")\n", - " print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 2: Ask a Quantum Computing Question\n", - "\n", - "Use RAG mode to get answers about quantum computing concepts:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 4 tools from MCP server:\n", - " - accept_model_disclaimer_tool\n", - " - get_completion_tool\n", - " - get_rag_completion_tool\n", - " - accept_completion_tool\n", - "Great question! Here's a comprehensive explanation of quantum entanglement:\n", - "\n", - "## What is Quantum Entanglement?\n", - "\n", - "**Quantum entanglement** is a fascinating phenomenon where two or more qubits become correlated such that the state of one qubit is dependent on the state of the other, **regardless of the distance between them**. This correlation is instantaneous and cannot be explained by classical physics.\n", - "\n", - "### Key Characteristics:\n", - "\n", - "- **Correlation**: Entangled qubits share a single quantum state. Measuring one qubit instantly affects the state of the other.\n", - "- **Superposition**: Entangled qubits can exist in multiple states simultaneously until measured.\n", - "- **Non-locality**: The correlation doesn't depend on physical distance—it's a fundamental quantum property.\n", - "\n", - "## Why is it Important?\n", - "\n", - "1. **Quantum Computing**: Entanglement is essential for quantum algorithms, enabling parallelism and exponential speedup for certain computational problems.\n", - "\n", - "2. **Quantum Communication**: Enables protocols like:\n", - " - **Quantum Teleportation**: Transmit a qubit's state from one location to another\n", - " - **Superdense Coding**: Send two classical bits using one qubit\n", - "\n", - "3. **Quantum Cryptography**: Used in quantum key distribution (QKD) for secure, unhackable communication.\n", - "\n", - "4. **Quantum Advantage**: Entanglement is one of the key resources that gives quantum computers their computational advantage over classical computers.\n", - "\n", - "### Common Examples:\n", - "\n", - "- **Bell States**: The simplest maximally entangled states of two qubits\n", - "- **GHZ States**: Entangled states of multiple qubits\n", - "\n", - "The example code provided shows how to create a Bell state (an entangled state) using Qiskit. Would you like me to generate code for a specific entanglement-based application, or do you have more questions about quantum entanglement?\n" - ] - } - ], - "source": [ - "async with mcp_client.session(\"qiskit-code-assistant\") as session:\n", - " agent = await create_agent_with_session(session)\n", - "\n", - " response = await ask_agent(agent, \"What is quantum entanglement and why is it important?\")\n", - " print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 3: Generate a VQE Circuit\n", - "\n", - "Let's generate more complex quantum code:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 4 tools from MCP server:\n", - " - accept_model_disclaimer_tool\n", - " - get_completion_tool\n", - " - get_rag_completion_tool\n", - " - accept_completion_tool\n", - "Perfect! I've generated a complete VQE (Variational Quantum Eigensolver) algorithm for finding the ground state energy. Here's what the code does:\n", - "\n", - "## Key Components:\n", - "\n", - "1. **Molecule Definition**: Uses PySCFDriver to define a hydrogen molecule (H2) with specific geometry\n", - "2. **Electronic Structure Problem**: Generates the quantum chemistry problem from the molecular structure\n", - "3. **Hamiltonian Mapping**: Converts the electronic Hamiltonian to qubit operators using the Parity mapper\n", - "4. **Ansatz Circuit**: Uses a TwoLocal parameterized circuit with:\n", - " - RY and RZ rotation blocks\n", - " - CZ entanglement gates\n", - " - Full entanglement pattern with 3 repetitions\n", - "5. **Optimizer**: Uses SPSA (Simultaneous Perturbation Stochastic Approximation) with 100 iterations\n", - "6. **VQE Execution**: Runs the variational algorithm to find the minimum eigenvalue\n", - "\n", - "## How VQE Works:\n", - "- It prepares a parameterized quantum circuit (ansatz)\n", - "- Measures the expectation value of the Hamiltonian\n", - "- Classically optimizes the circuit parameters to minimize energy\n", - "- Iterates until convergence\n", - "\n", - "## Notes:\n", - "- This example uses the hydrogen molecule as a simple demonstration\n", - "- For more complex molecules, you'd want to use larger basis sets and more sophisticated ansatz circuits\n", - "- The code requires `qiskit`, `qiskit-nature`, and `qiskit-algorithms` packages\n", - "\n", - "Would you like me to explain any specific part in more detail, or would you like to modify the code for a different molecule or ansatz structure?\n" - ] - } - ], - "source": [ - "async with mcp_client.session(\"qiskit-code-assistant\") as session:\n", - " agent = await create_agent_with_session(session)\n", - "\n", - " response = await ask_agent(\n", - " agent, \"Generate code to set up a simple VQE algorithm for finding the ground state energy\"\n", - " )\n", - " print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 4: Explain Qiskit Concepts\n", - "\n", - "Ask about Qiskit-specific features:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 4 tools from MCP server:\n", - " - accept_model_disclaimer_tool\n", - " - get_completion_tool\n", - " - get_rag_completion_tool\n", - " - accept_completion_tool\n", - "Great! Here's a comprehensive explanation of how Qiskit's transpiler works:\n", - "\n", - "## How Qiskit's Transpiler Works\n", - "\n", - "Qiskit's transpiler transforms abstract quantum circuits into forms suitable for execution on specific quantum devices. It operates through several key stages:\n", - "\n", - "### **Main Transpiler Stages**\n", - "\n", - "1. **Init Stage**: Decomposes multi-qubit gates into single- and two-qubit gates\n", - "2. **Layout Stage**: Maps virtual qubits to physical qubits on the device\n", - "3. **Routing Stage**: Inserts SWAP gates to match the circuit's connectivity to the device's topology\n", - "4. **Translation Stage**: Converts gates to the device's native basis set\n", - "5. **Optimization Stage**: Reduces circuit depth and gate count\n", - "6. **Scheduling Stage**: Inserts delay instructions to account for idle time\n", - "\n", - "### **Pass Managers**\n", - "\n", - "- **PassManager**: Executes a list of transpiler passes on a circuit\n", - "- **StagedPassManager**: Composed of multiple PassManagers, each representing a transpiler stage\n", - "\n", - "### **Key Customization Parameters**\n", - "\n", - "- **Optimization Level** (0-3): Controls the degree of optimization\n", - "- **Approximation Degree**: Trades output accuracy for ease of execution\n", - "- **Initial Layout**: Specifies the initial mapping of virtual to physical qubits\n", - "- **Random Seed**: Ensures reproducible results\n", - "- **Basis Gates**: Defines the set of gates supported by the target device\n", - "\n", - "### **Example Workflow**\n", - "\n", - "The typical process involves:\n", - "1. Creating a quantum circuit\n", - "2. Generating a preset pass manager for your target backend\n", - "3. Running the transpiler to optimize the circuit for that specific device\n", - "\n", - "### **Extensibility**\n", - "\n", - "You can customize the transpiler through:\n", - "- **Preset Pass Managers**: Use `generate_preset_pass_manager` with various parameters\n", - "- **Custom Pass Managers**: Create your own with specific passes\n", - "- **Plugins**: Install third-party plugins for extended functionality (layout, routing, unitary synthesis, etc.)\n", - "\n", - "The transpiler is essential for bridging the gap between high-level quantum algorithms and the physical constraints of real quantum hardware!\n" - ] - } - ], - "source": [ - "async with mcp_client.session(\"qiskit-code-assistant\") as session:\n", - " agent = await create_agent_with_session(session)\n", - "\n", - " response = await ask_agent(agent, \"How does Qiskit's transpiler work?\")\n", - " print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 5: Interactive Chat\n", - "\n", - "Run this cell to have an interactive conversation with the agent:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 4 tools from MCP server:\n", - " - accept_model_disclaimer_tool\n", - " - get_completion_tool\n", - " - get_rag_completion_tool\n", - " - accept_completion_tool\n", - "Agent ready! Type your questions below.\n", - "Enter 'quit' to stop.\n", - "\n", - "\n", - "Assistant: Hello! 👋 Welcome to the Qiskit Code Assistant! \n", - "\n", - "I'm here to help you with quantum computing and Qiskit programming. I can assist you with:\n", - "\n", - "1. **Generating Qiskit Code** - Create quantum circuits, algorithms, and programs based on your descriptions\n", - "2. **Answering Conceptual Questions** - Explain quantum computing concepts, Qiskit features, and best practices\n", - "3. **Code Explanations** - Help you understand how quantum code works\n", - "\n", - "What would you like to do today? For example, you could ask me to:\n", - "- \"Create a Bell state circuit\"\n", - "- \"Explain quantum entanglement\"\n", - "- \"Build a quantum random number generator\"\n", - "- \"How does the Qiskit transpiler work?\"\n", - "\n", - "Feel free to ask anything related to quantum computing or Qiskit! 🚀\n", - "\n", - "\n", - "Assistant: Great! Here's a comprehensive overview of **AI-powered transpiler passes** in Qiskit:\n", - "\n", - "## AI-Powered Transpiler Passes\n", - "\n", - "AI-powered transpiler passes are advanced optimization techniques that use artificial intelligence to improve quantum circuit transpilation. They serve as drop-in replacements for traditional Qiskit passes and often produce better results with lower circuit depth and CNOT count, while being faster than traditional optimization algorithms.\n", - "\n", - "### Types of AI Passes\n", - "\n", - "1. **Routing Passes**:\n", - " - `AIRouting` - Handles layout selection and circuit routing\n", - "\n", - "2. **Circuit Synthesis Passes**:\n", - " - `AICliffordSynthesis` - Optimizes Clifford circuits\n", - " - `AILinearFunctionSynthesis` - Optimizes linear function circuits\n", - " - `AIPermutationSynthesis` - Optimizes permutation circuits\n", - " - `AIPauliNetworkSynthesis` - Optimizes Pauli Network circuits\n", - "\n", - "### Execution Modes\n", - "\n", - "- **Local Mode**: Run locally with extra dependencies (`pip install qiskit-ibm-transpiler[ai-local-mode]`)\n", - "- **Cloud Mode**: Run via the Qiskit Transpiler Service (requires IBM Quantum Premium Plan, Flex Plan, or On-Prem Plan)\n", - "\n", - "### Key Configuration Options\n", - "\n", - "- **Optimization Level**: 1, 2, or 3 (higher levels provide better optimization but take longer)\n", - "- **Layout Mode**: `keep`, `improve`, or `optimize`\n", - "- **Local Mode**: Automatically falls back to cloud if local dependencies aren't available\n", - "\n", - "### Benefits\n", - "\n", - "- **Improved Circuit Quality**: Produces shorter and shallower circuits\n", - "- **Speed**: Faster than traditional optimization algorithms like SAT solvers\n", - "- **Flexibility**: Integrates seamlessly into existing Qiskit workflows\n", - "\n", - "These AI passes represent a significant advancement in quantum circuit optimization, making it easier to prepare circuits for execution on quantum hardware!\n", - "\n", - "Goodbye!\n" - ] - } - ], - "source": [ - "async with mcp_client.session(\"qiskit-code-assistant\") as session:\n", - " agent = await create_agent_with_session(session)\n", - " print(\"Agent ready! Type your questions below.\")\n", - " print(\"Enter 'quit' to stop.\\n\")\n", - "\n", - " while True:\n", - " try:\n", - " query = input(\"You: \").strip()\n", - " if not query:\n", - " continue\n", - " if query.lower() in [\"quit\", \"exit\", \"q\"]:\n", - " print(\"Goodbye!\")\n", - " break\n", - "\n", - " response = await ask_agent(agent, query)\n", - " print(f\"\\nAssistant: {response}\\n\")\n", - " except KeyboardInterrupt:\n", - " print(\"\\nGoodbye!\")\n", - " break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Custom Queries\n", - "\n", - "Use this cell to ask the agent any question:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 4 tools from MCP server:\n", - " - accept_model_disclaimer_tool\n", - " - get_completion_tool\n", - " - get_rag_completion_tool\n", - " - accept_completion_tool\n", - "Perfect! Here's a 3-qubit circuit with a Toffoli gate:\n", - "\n", - "```python\n", - "from qiskit.circuit.library import CCXGate\n", - "from qiskit import QuantumCircuit\n", - "\n", - "# Create a 3-qubit circuit\n", - "qc = QuantumCircuit(3)\n", - "\n", - "# Apply a Toffoli (CCX) gate\n", - "qc.append(CCXGate(), [0, 1, 2])\n", - "\n", - "# Draw the circuit\n", - "qc.draw(\"mpl\")\n", - "```\n", - "\n", - "**Explanation:**\n", - "\n", - "- **QuantumCircuit(3)**: Creates a quantum circuit with 3 qubits\n", - "- **CCXGate()**: This is the Toffoli gate (also known as the Controlled-Controlled-X gate)\n", - "- **qc.append()**: Applies the gate to the specified qubits [0, 1, 2]\n", - " - Qubits 0 and 1 are the **control qubits**\n", - " - Qubit 2 is the **target qubit**\n", - "\n", - "The Toffoli gate is a 3-qubit gate that flips the target qubit (qubit 2) only if both control qubits (qubits 0 and 1) are in the |1⟩ state. It's a fundamental gate for quantum computing and is universal for reversible classical computation.\n" - ] - } - ], - "source": [ - "# Enter your question here:\n", - "MY_QUESTION = \"Write a circuit with 3 qubits that applies a Toffoli gate\"\n", - "\n", - "async with mcp_client.session(\"qiskit-code-assistant\") as session:\n", - " agent = await create_agent_with_session(session)\n", - " response = await ask_agent(agent, MY_QUESTION)\n", - " print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Available Tools\n", - "\n", - "The agent has access to these tools provided by the MCP server:\n", - "\n", - "| Tool | Description |\n", - "|------|-------------|\n", - "| `get_completion_tool` | Generate Qiskit code completions based on prompts |\n", - "| `get_rag_completion_tool` | Answer questions about Qiskit and quantum computing using RAG |\n", - "| `accept_model_disclaimer_tool` | Accept the disclaimer for a model |\n", - "| `accept_completion_tool` | Accept/acknowledge a generated completion |" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Troubleshooting\n", - "\n", - "### Authentication errors?\n", - "- Verify your `QISKIT_IBM_TOKEN` is correct\n", - "- Make sure you have access to Qiskit Code Assistant\n", - "- Get your token from: https://quantum.ibm.com/\n", - "\n", - "### MCP server not found?\n", - "Make sure `qiskit-code-assistant-mcp-server` is installed: `pip install qiskit-code-assistant-mcp-server[examples]`\n", - "\n", - "### Model disclaimer required?\n", - "The agent will automatically use `accept_model_disclaimer_tool` when needed." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/qiskit-code-assistant-mcp-server/examples/langchain_agent.py b/qiskit-code-assistant-mcp-server/examples/langchain_agent.py deleted file mode 100644 index ef2d83b..0000000 --- a/qiskit-code-assistant-mcp-server/examples/langchain_agent.py +++ /dev/null @@ -1,394 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -LangChain Agent Example with Qiskit Code Assistant MCP Server - -This example demonstrates how to create an AI agent using LangChain that -connects to the qiskit-code-assistant-mcp-server via the Model Context Protocol (MCP). - -The agent can interact with Qiskit Code Assistant to: -- Generate quantum code completions -- Answer questions about Qiskit and quantum computing (RAG mode) -- Accept model disclaimers -- Track completion acceptance - -Supported LLM Providers: - - OpenAI (default): pip install langchain-openai - - Anthropic: pip install langchain-anthropic - - Ollama (local): pip install langchain-ollama - - Google: pip install langchain-google-genai - - Watsonx: pip install langchain-ibm - -Requirements: - pip install langchain langchain-mcp-adapters python-dotenv - pip install # See above for your chosen provider - -Usage: - # With OpenAI (default) - export OPENAI_API_KEY="your-api-key" - export QISKIT_IBM_TOKEN="your-ibm-quantum-token" - python langchain_agent.py - - # With Anthropic - export ANTHROPIC_API_KEY="your-api-key" - export QISKIT_IBM_TOKEN="your-ibm-quantum-token" - python langchain_agent.py --provider anthropic - - # With Ollama (local, no API key needed) - export QISKIT_IBM_TOKEN="your-ibm-quantum-token" - python langchain_agent.py --provider ollama --model llama3.3 - - # Single query mode - python langchain_agent.py --single -""" - -import argparse -import asyncio -import os - -from dotenv import load_dotenv -from langchain.agents import create_agent -from langchain_core.language_models.chat_models import BaseChatModel -from langchain_mcp_adapters.client import MultiServerMCPClient -from langchain_mcp_adapters.tools import load_mcp_tools - - -# Load environment variables -load_dotenv() - -SYSTEM_PROMPT = """You are a helpful quantum computing coding assistant with access to Qiskit Code Assistant -through the MCP server. - -You can help users: -- Generate quantum code using Qiskit (get_completion_tool) -- Answer questions about Qiskit and quantum computing concepts (get_rag_completion_tool) -- Accept model disclaimers when required (accept_model_disclaimer_tool) -- Track completion acceptance for feedback (accept_completion_tool) - -When generating code: -- Use the get_completion_tool to generate Qiskit code based on user prompts -- Provide clear explanations of the generated code -- Suggest improvements or alternatives when appropriate - -When answering questions: -- Use the get_rag_completion_tool for conceptual questions about quantum computing -- Provide accurate and educational explanations -- Reference Qiskit documentation when relevant - -Always provide clear explanations about quantum computing concepts when relevant. -If an operation fails, explain the error and suggest possible solutions.""" - - -def get_llm(provider: str, model: str | None = None) -> BaseChatModel: - """ - Get an LLM instance for the specified provider. - - Args: - provider: The LLM provider ('openai', 'anthropic', 'ollama', 'google', 'watsonx'). - model: Optional model name override. - - Returns: - Configured LLM instance. - - Raises: - ValueError: If provider is not supported or required package is missing. - """ - if provider == "openai": - try: - from langchain_openai import ChatOpenAI - except ImportError: - raise ValueError("Install langchain-openai: pip install langchain-openai") - return ChatOpenAI(model=model or "gpt-5.2", temperature=0) - - elif provider == "anthropic": - try: - from langchain_anthropic import ChatAnthropic - except ImportError: - raise ValueError("Install langchain-anthropic: pip install langchain-anthropic") - return ChatAnthropic(model=model or "claude-sonnet-4-5-20250929", temperature=0) - - elif provider == "ollama": - try: - from langchain_ollama import ChatOllama - except ImportError: - raise ValueError("Install langchain-ollama: pip install langchain-ollama") - return ChatOllama(model=model or "llama3.3", temperature=0) - - elif provider == "google": - try: - from langchain_google_genai import ChatGoogleGenerativeAI - except ImportError: - raise ValueError("Install langchain-google-genai: pip install langchain-google-genai") - return ChatGoogleGenerativeAI(model=model or "gemini-3-pro-preview", temperature=0) - - elif provider == "watsonx": - try: - from langchain_ibm import ChatWatsonx - except ImportError: - raise ValueError("Install langchain-ibm: pip install langchain-ibm") - return ChatWatsonx( - model_id=model or "ibm/granite-4-h-small", - url=os.getenv("WATSONX_URL", "https://us-south.ml.cloud.ibm.com"), - project_id=os.getenv("WATSONX_PROJECT_ID"), - params={ - "temperature": 0, - "max_tokens": 4096, - }, - ) - - else: - raise ValueError( - f"Unknown provider: {provider}. Supported: openai, anthropic, ollama, google, watsonx" - ) - - -def check_api_key(provider: str) -> bool: - """Check if required API key/config is set for the provider.""" - key_map = { - "openai": ["OPENAI_API_KEY"], - "anthropic": ["ANTHROPIC_API_KEY"], - "google": ["GOOGLE_API_KEY"], - "ollama": [], # No API key needed for local Ollama - "watsonx": ["WATSONX_APIKEY", "WATSONX_PROJECT_ID"], - } - - # Always need QISKIT_IBM_TOKEN for the MCP server - required_keys = [*key_map.get(provider, []), "QISKIT_IBM_TOKEN"] - missing_keys = [key for key in required_keys if not os.getenv(key)] - - if not missing_keys: - return True - - print(f"Error: Missing required environment variables for {provider}:") - for key in missing_keys: - print(f" - {key}") - print("\nSet them with:") - for key in missing_keys: - print(f" export {key}='your-value'") - return False - - -def get_mcp_client() -> MultiServerMCPClient: - """ - Create and return an MCP client configured for the Qiskit Code Assistant server. - - Returns: - Configured MultiServerMCPClient instance. - """ - return MultiServerMCPClient( - { - "qiskit-code-assistant": { - "transport": "stdio", - "command": "qiskit-code-assistant-mcp-server", - "args": [], - "env": { - "QISKIT_IBM_TOKEN": os.getenv("QISKIT_IBM_TOKEN", ""), - }, - } - } - ) - - -async def create_quantum_agent_with_session( - session, provider: str = "openai", model: str | None = None -): - """ - Create a LangChain agent using an existing MCP session. - - This uses a persistent session to avoid spawning a new server process - for each tool call, significantly improving performance. - - Args: - session: An active MCP ClientSession from MultiServerMCPClient.session() - provider: The LLM provider. - model: Optional model name override. - - Returns: - Configured LangChain agent. - """ - # Load tools from the existing session (reuses the same server process) - tools = await load_mcp_tools(session) - - # Get the LLM for the specified provider - llm = get_llm(provider, model) - - # Create an agent using LangChain's create_agent - agent = create_agent(llm, tools, system_prompt=SYSTEM_PROMPT) - - return agent - - -async def run_agent_query(agent, query: str, history: list | None = None) -> tuple[str, list]: - """ - Run a query through the agent with conversation history. - - Args: - agent: The configured LangChain agent. - query: The user's query. - history: Optional list of previous messages for context. - - Returns: - Tuple of (response_text, updated_history). - """ - from langchain_core.messages import HumanMessage - - # Build messages with history - messages = list(history) if history else [] - messages.append(HumanMessage(content=query)) - - result = await agent.ainvoke({"messages": messages}) - result_messages = result.get("messages", []) - - if result_messages: - response = result_messages[-1].content - # Return the full conversation history from the agent - return response, result_messages - - return "No response generated.", messages - - -async def interactive_session(provider: str, model: str | None): - """Run an interactive session with the quantum coding agent.""" - if not check_api_key(provider): - return - - print("Qiskit Code Assistant Agent with LangChain + MCP") - print("=" * 50) - print(f"Provider: {provider}" + (f" (model: {model})" if model else "")) - print("This agent connects to the qiskit-code-assistant-mcp-server") - print("to help you write and understand quantum code.") - print("Type 'quit' to exit, 'clear' to reset conversation history.\n") - - # Example queries to demonstrate capabilities - example_queries = [ - "Write a quantum circuit that creates a Bell state", - "What is quantum entanglement?", - "Generate code to run a VQE algorithm", - "Explain how Qiskit's transpiler works", - "Write a circuit with 3 qubits and apply a Toffoli gate", - ] - - print("Example queries you can try:") - for i, query in enumerate(example_queries, 1): - print(f" {i}. {query}") - print() - - print("Connecting to MCP server...") - - # Use persistent session to avoid spawning new server for each tool call - mcp_client = get_mcp_client() - async with mcp_client.session("qiskit-code-assistant") as session: - agent = await create_quantum_agent_with_session(session, provider, model) - print("Connected! Ready to help with quantum code.\n") - - # Maintain conversation history for context - history: list = [] - - while True: - try: - user_input = input("You: ").strip() - if not user_input: - continue - if user_input.lower() in ["quit", "exit", "q"]: - print("Goodbye!") - break - if user_input.lower() == "clear": - history = [] - print("Conversation history cleared.\n") - continue - - response, history = await run_agent_query(agent, user_input, history) - print(f"\nAssistant: {response}\n") - - except KeyboardInterrupt: - print("\nGoodbye!") - break - except Exception as e: - print(f"Error: {e}\n") - - -async def single_query_example(provider: str, model: str | None): - """Example of running a single query programmatically.""" - if not check_api_key(provider): - return - - print("Running single query example...") - print(f"Provider: {provider}" + (f" (model: {model})" if model else "")) - print("-" * 40) - - # Use persistent session for the query - mcp_client = get_mcp_client() - async with mcp_client.session("qiskit-code-assistant") as session: - agent = await create_quantum_agent_with_session(session, provider, model) - - # Run a sample query - response, _ = await run_agent_query( - agent, "Write a Qiskit circuit that creates a GHZ state with 3 qubits" - ) - print(f"\nResponse:\n{response}") - - -def main(): - """Entry point for the example.""" - parser = argparse.ArgumentParser( - description="LangChain Agent for Qiskit Code Assistant via MCP", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Examples: - # OpenAI (default) - python langchain_agent.py - - # Anthropic Claude - python langchain_agent.py --provider anthropic - - # Local Ollama - python langchain_agent.py --provider ollama --model llama3.3 - - # Google Gemini - python langchain_agent.py --provider google - - # IBM Watsonx - python langchain_agent.py --provider watsonx - - # Single query mode - python langchain_agent.py --single - """, - ) - parser.add_argument( - "--provider", - choices=["openai", "anthropic", "ollama", "google", "watsonx"], - default="openai", - help="LLM provider to use (default: openai)", - ) - parser.add_argument( - "--model", - type=str, - default=None, - help="Model name override (uses provider default if not specified)", - ) - parser.add_argument( - "--single", - action="store_true", - help="Run a single example query instead of interactive mode", - ) - - args = parser.parse_args() - - if args.single: - asyncio.run(single_query_example(args.provider, args.model)) - else: - asyncio.run(interactive_session(args.provider, args.model)) - - -if __name__ == "__main__": - main() diff --git a/qiskit-code-assistant-mcp-server/pyproject.toml b/qiskit-code-assistant-mcp-server/pyproject.toml deleted file mode 100644 index 95bb1c6..0000000 --- a/qiskit-code-assistant-mcp-server/pyproject.toml +++ /dev/null @@ -1,94 +0,0 @@ -[project] -name = "qiskit-code-assistant-mcp-server" -version = "0.4.0" -description = "MCP server for the Qiskit Code Assistant" -readme = "README.md" -requires-python = ">=3.10,<3.15" -dependencies = [ - "fastmcp>=2.8.1,<3", - "python-dotenv>=1.0.0", - "httpx>=0.25.0", - "nest-asyncio>=1.5.0", -] - -[project.optional-dependencies] -dev = [ - "ruff>=0.11.13", - "mypy>=1.15.0", - "pre-commit>=3.5.0", - "bandit[toml]>=1.7.0", -] -test = [ - "pytest>=7.4.0", - "pytest-asyncio>=0.21.0", - "pytest-mock>=3.11.0", - "pytest-cov>=4.1.0", - "httpx>=0.25.0", - "respx>=0.20.0", -] -all = [ - "qiskit-code-assistant-mcp-server[dev,test]", -] -[[project.authors]] -name = "Quantum+AI Team. IBM Quantum" -email = "Quantum.Plus.AI@ibm.com" - -[project.license] -text = "Apache-2.0" - -keywords = ["quantum", "qiskit", "mcp", "ai", "code-assistant", "quantum-computing", "llm"] -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "Topic :: Scientific/Engineering :: Physics", - "Topic :: Software Development :: Libraries :: Python Modules", -] - -[project.urls] -Homepage = "https://github.com/Qiskit/mcp-servers" -Repository = "https://github.com/Qiskit/mcp-servers" -Documentation = "https://github.com/Qiskit/mcp-servers/tree/main/qiskit-code-assistant-mcp-server#readme" -"Bug Tracker" = "https://github.com/Qiskit/mcp-servers/issues" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["src/qiskit_code_assistant_mcp_server"] - -[project.scripts] -qiskit-code-assistant-mcp-server = "qiskit_code_assistant_mcp_server:main" - -[dependency-groups] -dev = [ - "ruff>=0.11.13", - "mypy>=1.15.0", - "bandit[toml]>=1.9.2", -] -test = [ - "pytest>=7.4.0", - "pytest-asyncio>=0.21.0", - "pytest-mock>=3.11.0", - "pytest-cov>=4.1.0", - "httpx>=0.25.0", - "respx>=0.20.0", -] -examples = [ - "langchain>=1.2.0", - "langchain-mcp-adapters>=0.1.0", - "langchain-anthropic>=1.0.0", - "python-dotenv>=1.0.0", -] - -# Bandit security configuration -[tool.bandit] -skips = ["B101"] # Skip assert_used check in tests -exclude_dirs = ["tests", ".venv", "build", "dist"] diff --git a/qiskit-code-assistant-mcp-server/pytest.ini b/qiskit-code-assistant-mcp-server/pytest.ini deleted file mode 100644 index fb43f37..0000000 --- a/qiskit-code-assistant-mcp-server/pytest.ini +++ /dev/null @@ -1,17 +0,0 @@ -[pytest] -testpaths = tests -python_files = test_*.py -python_classes = Test* -python_functions = test_* -addopts = - -v - --tb=short - --strict-markers - --disable-warnings -asyncio_mode = auto -asyncio_default_fixture_loop_scope = function -markers = - asyncio: marks tests as async (deselect with '-m "not asyncio"') - integration: marks tests as integration tests - unit: marks tests as unit tests - slow: marks tests as slow running \ No newline at end of file diff --git a/qiskit-code-assistant-mcp-server/run_tests.sh b/qiskit-code-assistant-mcp-server/run_tests.sh deleted file mode 100755 index 4ca399e..0000000 --- a/qiskit-code-assistant-mcp-server/run_tests.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -# Qiskit Code Assistant MCP Server - Test Runner Script - -set -e - -echo "🧪 Running Qiskit Code Assistant MCP Server Tests" -echo "==================================================" - -# Install test dependencies -echo "📦 Installing test dependencies..." -uv sync --group dev --group test - -# Run linting -echo "" -echo "🔍 Running code linting..." -uv run ruff check src tests -uv run ruff format --check src tests - -# Run type checking -echo "" -echo "🔬 Running type checking..." -uv run mypy src - -# Run unit tests -echo "" -echo "🧪 Running unit tests..." -uv run pytest tests/ -v -m "not integration" --cov=src --cov-report=term-missing - -# Run integration tests -echo "" -echo "🔗 Running integration tests..." -uv run pytest tests/ -v -m "integration" --cov=src --cov-append --cov-report=term-missing - -# Generate coverage report -echo "" -echo "📊 Generating coverage report..." -uv run pytest tests/ --cov=src --cov-report=html --cov-report=xml - -echo "" -echo "✅ All tests completed successfully!" -echo "📋 Coverage report generated in htmlcov/index.html" - -# Assisted by watsonx Code Assistant \ No newline at end of file diff --git a/qiskit-code-assistant-mcp-server/server.json b/qiskit-code-assistant-mcp-server/server.json deleted file mode 100644 index 2137e9e..0000000 --- a/qiskit-code-assistant-mcp-server/server.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", - "name": "io.github.Qiskit/qiskit-code-assistant-mcp-server", - "title": "Qiskit Code Assistant MCP Server", - "description": "MCP server for the Qiskit Code Assistant intelligent code completion", - "version": "0.4.0", - "websiteUrl": "https://github.com/Qiskit/mcp-servers/tree/main/qiskit-code-assistant-mcp-server", - "repository": { - "url": "https://github.com/Qiskit/mcp-servers", - "source": "github", - "id": "1051018539", - "subfolder": "qiskit-code-assistant-mcp-server" - }, - "packages": [ - { - "registryType": "pypi", - "identifier": "qiskit-code-assistant-mcp-server", - "version": "0.4.0", - "runtimeHint": "uvx", - "transport": { - "type": "stdio" - }, - "environmentVariables": [ - { - "name": "QISKIT_IBM_TOKEN", - "description": "Your IBM Quantum API token for authentication", - "isRequired": true, - "isSecret": true - }, - { - "name": "QCA_TOOL_API_BASE", - "description": "Qiskit Code Assistant API base URL", - "default": "https://qiskit-code-assistant.quantum.ibm.com" - }, - { - "name": "QCA_TOOL_MODEL_NAME", - "description": "Default model name for code completion", - "default": "mistral-small-3.2-24b-qiskit" - } - ] - } - ] -} diff --git a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/__init__.py b/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/__init__.py deleted file mode 100644 index d7b351f..0000000 --- a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -from qiskit_code_assistant_mcp_server import server - - -def main() -> None: - """Main entry point for the package.""" - server.mcp.run(transport="stdio", show_banner=False) - - -# Optionally expose other important items at package level -__all__ = ["main", "server"] diff --git a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/constants.py b/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/constants.py deleted file mode 100644 index 2f89401..0000000 --- a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/constants.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -import importlib.metadata -import logging -import os - -from dotenv import load_dotenv - - -# Load environment variables -load_dotenv() - -logger = logging.getLogger(__name__) - - -################################################## -## CONSTANTS -################################################## - -try: - QCA_TOOL_X_CALLER = f"qiskit-code-assistant-mcp-server/{importlib.metadata.version('qiskit-code-assistant-mcp-server')}" -except Exception: - QCA_TOOL_X_CALLER = "qiskit-code-assistant-mcp-server/unknown" - logger.warning("Could not determine package version for X-Caller header") - -QCA_REQUEST_TIMEOUT = float(os.getenv("QCA_REQUEST_TIMEOUT", "30.0")) - -# Validate timeout -if QCA_REQUEST_TIMEOUT <= 0 or QCA_REQUEST_TIMEOUT > 300: - logger.warning(f"Invalid request timeout {QCA_REQUEST_TIMEOUT}, using default 30.0") - QCA_REQUEST_TIMEOUT = 30.0 - - -################################################## -## ENVIRONMENT VARIABLES -################################################## - -QCA_TOOL_API_BASE = os.getenv( - "QCA_TOOL_API_BASE", "https://qiskit-code-assistant.quantum.ibm.com" -).rstrip("/") # Remove trailing slash for consistent URLs - -QCA_TOOL_MODEL_NAME = os.getenv("QCA_TOOL_MODEL_NAME", "mistral-small-3.2-24b-qiskit") - -# Validate log level -QCA_MCP_DEBUG_LEVEL = os.getenv("QCA_MCP_DEBUG_LEVEL", "INFO").upper() -if QCA_MCP_DEBUG_LEVEL not in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]: - logger.warning(f"Invalid debug level {QCA_MCP_DEBUG_LEVEL}, using INFO") - QCA_MCP_DEBUG_LEVEL = "INFO" - - -################################################## -## CONFIGURATION VALIDATION -################################################## - - -def validate_configuration() -> bool: - """Validate the current configuration and log any issues.""" - valid = True - - if not QCA_TOOL_API_BASE.startswith(("http://", "https://")): - logger.error(f"Invalid API base URL: {QCA_TOOL_API_BASE}") - valid = False - - if not QCA_TOOL_MODEL_NAME: - logger.error("Model name cannot be empty") - valid = False - - return valid diff --git a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/py.typed b/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/qca.py b/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/qca.py deleted file mode 100644 index 657a7a9..0000000 --- a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/qca.py +++ /dev/null @@ -1,407 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -import asyncio -import logging -from typing import Any - -from qiskit_code_assistant_mcp_server.constants import ( - QCA_REQUEST_TIMEOUT, - QCA_TOOL_API_BASE, - QCA_TOOL_MODEL_NAME, -) -from qiskit_code_assistant_mcp_server.utils import ( - close_http_client, - make_qca_request, - with_sync, -) - - -logger = logging.getLogger(__name__) - - -@with_sync -async def list_models() -> dict[str, Any]: - """List the available models from the Qiskit Code Assistant.""" - try: - logger.info("Fetching available models from Qiskit Code Assistant") - url = f"{QCA_TOOL_API_BASE}/v1/models" - data = await make_qca_request(url, method="GET") - - if "error" in data: - logger.error(f"Failed to list models: {data['error']}") - return {"status": "error", "message": data["error"]} - - models = data.get("data", []) - if len(models) <= 0: - logger.warning("No models retrieved from Qiskit Code Assistant") - return {"status": "error", "message": "No models retrieved."} - else: - logger.info(f"Retrieved {len(models)} models from Qiskit Code Assistant") - return {"status": "success", "models": models} - except Exception as e: - logger.error(f"Exception in list_models: {e!s}") - return {"status": "error", "message": f"Failed to list models: {e!s}"} - - -def _select_available_model() -> str: - """ - Select an available model from the Qiskit Code Assistant service. - - This function checks if the configured default model is available. - If not, it selects the first available model as a fallback. - - Returns: - The model name to use for completions - """ - try: - # Run the async list_models function synchronously - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - models_result = loop.run_until_complete(list_models()) - # Close the HTTP client since it's attached to this loop which we are about to close - loop.run_until_complete(close_http_client()) - finally: - loop.close() - - if models_result.get("status") == "success": - available_models = models_result.get("models", []) - model_ids: list[str] = [ - model.get("id") for model in available_models if model.get("id") - ] - - # Check if default model is available - if QCA_TOOL_MODEL_NAME in model_ids: - logger.info(f"Default model '{QCA_TOOL_MODEL_NAME}' is available") - return QCA_TOOL_MODEL_NAME - - # Default model not available, use first available model - if model_ids: - selected_model = model_ids[0] - logger.warning( - f"Default model '{QCA_TOOL_MODEL_NAME}' is not available. " - f"Using '{selected_model}' instead. " - f"Available models: {', '.join(model_ids)}" - ) - return selected_model - - # No models available - logger.error("No models available from Qiskit Code Assistant service") - else: - error_msg = models_result.get("message", "Unknown error") - logger.error(f"Failed to fetch available models: {error_msg}") - - except Exception as e: - logger.error(f"Exception while selecting available model: {e!s}") - - # Fallback to configured default if anything goes wrong - logger.warning( - f"Unable to verify model availability. Using configured default: {QCA_TOOL_MODEL_NAME}" - ) - return QCA_TOOL_MODEL_NAME - - -# Select the model to use at module initialization -_SELECTED_MODEL_NAME = _select_available_model() -logger.info(f"Using model: {_SELECTED_MODEL_NAME}") - - -@with_sync -async def get_model(model_id: str) -> dict[str, Any]: - """Get the info for a model from the Qiskit Code Assistant. - - Args: - model_id: The ID of the model to retrieve - """ - if not model_id or not model_id.strip(): - return { - "status": "error", - "message": "model_id is required and cannot be empty", - } - - try: - logger.info(f"Fetching model info for model_id: {model_id}") - url = f"{QCA_TOOL_API_BASE}/v1/models/{model_id}" - data = await make_qca_request(url, method="GET") - - if "error" in data: - logger.error(f"Failed to get model {model_id}: {data['error']}") - return {"status": "error", "message": data["error"]} - elif "id" not in data: - logger.warning(f"Model {model_id} not retrieved - missing ID in response") - return {"status": "error", "message": "Model not retrieved."} - else: - logger.info(f"Successfully retrieved model {model_id}") - return {"status": "success", "model": data} - except Exception as e: - logger.error(f"Exception in get_model for {model_id}: {e!s}") - return {"status": "error", "message": f"Failed to get model: {e!s}"} - - -@with_sync -async def get_model_disclaimer(model_id: str) -> dict[str, Any]: - """Get the disclaimer for a model from the Qiskit Code Assistant. - - Args: - model_id: The ID of the model for which we want to retrieve the disclaimer - """ - try: - url = f"{QCA_TOOL_API_BASE}/v1/models/{model_id}/disclaimer" - data = await make_qca_request(url, method="GET") - - if "error" in data: - return {"status": "error", "message": data["error"]} - elif "id" not in data: - return {"status": "error", "message": "Model disclaimer not retrieved."} - else: - return {"status": "success", "disclaimer": data} - except Exception as e: - return { - "status": "error", - "message": f"Failed to get model disclaimer: {e!s}", - } - - -@with_sync -async def accept_model_disclaimer(model_id: str, disclaimer_id: str) -> dict[str, Any]: - """ - Accept the disclaimer for an available model from the Qiskit Code Assistant. - - Args: - model_id: The ID of the model for which we want to accept the disclaimer - disclaimer_id: The ID of the disclaimer we want to accept - - Returns: - Disclaimer acceptance status - """ - try: - url = f"{QCA_TOOL_API_BASE}/v1/models/{model_id}/disclaimer" - body = {"disclaimer": disclaimer_id, "accepted": "true"} - data = await make_qca_request(url, method="POST", body=body) - - if "error" in data: - return {"status": "error", "message": data["error"]} - elif "success" not in data: - return { - "status": "error", - "message": "The response does not contain the acceptance result.", - } - else: - return {"status": "success", "result": data} - except Exception as e: - return {"status": "error", "message": f"Failed to accept disclaimer: {e!s}"} - - -@with_sync -async def get_completion(prompt: str) -> dict[str, Any]: - """ - Get code completion for writing, completing, and optimizing quantum code using Qiskit. - - This tool generates Qiskit Python code based on the given prompt. Use it when users - want to create quantum circuits, algorithms, or any Qiskit-related code. - - Args: - prompt: Natural language description of what code to generate. Be specific about: - - What quantum circuit or algorithm to create - - Number of qubits if relevant - - Any specific gates or operations needed - - Returns: - Dictionary with: - - status: 'success' or 'error' - - code: The generated Python/Qiskit code (primary output - display this to user) - - completion_id: ID for accepting/tracking this completion - - message: Error description if status is 'error' - - Example successful response: - { - "status": "success", - "code": "from qiskit import QuantumCircuit\\n\\nqc = QuantumCircuit(2)\\nqc.h(0)\\nqc.cx(0, 1)", - "completion_id": "cmpl_abc123" - } - """ - if not prompt or not prompt.strip(): - return {"status": "error", "message": "prompt is required and cannot be empty"} - - if len(prompt.strip()) > 10000: # Reasonable limit - return { - "status": "error", - "message": "prompt is too long (max 10000 characters)", - } - - try: - logger.info(f"Requesting code completion for prompt (length: {len(prompt)})") - url = f"{QCA_TOOL_API_BASE}/v1/chat/completions" - body = { - "model": _SELECTED_MODEL_NAME, - "messages": [{"role": "user", "content": prompt.strip()}], - } - data = await make_qca_request(url, method="POST", body=body) - - if "error" in data: - logger.error(f"Failed to get completion: {data['error']}") - return {"status": "error", "message": data["error"]} - - choices = data.get("choices") - completion_id = data.get("id") - - if not choices: - logger.warning("No choices returned for completion request") - return {"status": "error", "message": "No choices for this prompt."} - else: - # Extract the primary code from the first choice for easy LLM access - primary_code = choices[0].get("message", {}).get("content", "") if choices else "" - logger.info( - f"Successfully generated completion with {len(choices)} choices (ID: {completion_id})" - ) - return { - "status": "success", - "code": primary_code, - "completion_id": completion_id, - } - except Exception as e: - logger.error(f"Exception in get_completion: {e!s}") - return {"status": "error", "message": f"Failed to get completion: {e!s}"} - - -@with_sync -async def get_rag_completion(prompt: str) -> dict[str, Any]: - """ - Get RAG (Retrieval-Augmented Generation) completion for answering questions about - Qiskit and quantum computing concepts. - - This tool uses IBM's knowledge base to answer conceptual questions. Use it when users - ask about quantum computing theory, Qiskit features, best practices, or need explanations - rather than code generation. - - When to use this vs get_completion: - - Use get_rag_completion for: "What is entanglement?", "How does the transpiler work?" - - Use get_completion for: "Write a Bell state circuit", "Generate VQE code" - - Args: - prompt: A question about Qiskit or quantum computing concepts. - Examples: "What are the different types of quantum gates?" - "How do I optimize circuit depth?" - - Returns: - Dictionary with: - - status: 'success' or 'error' - - answer: The explanatory text response (primary output - display this to user) - - completion_id: ID for accepting/tracking this completion - - message: Error description if status is 'error' - - Example successful response: - { - "status": "success", - "answer": "Quantum entanglement is a phenomenon where two qubits...", - "completion_id": "cmpl_xyz789" - } - """ - try: - url = f"{QCA_TOOL_API_BASE}/v1/chat/completions" - body = { - "model": _SELECTED_MODEL_NAME, - "mode": "rag", - "messages": [{"role": "user", "content": prompt.strip()}], - } - data = await make_qca_request(url, method="POST", body=body) - - if "error" in data: - return {"status": "error", "message": data["error"]} - - choices = data.get("choices") - if not choices: - return {"status": "error", "message": "No choices for this prompt."} - else: - # Extract the primary answer from the first choice for easy LLM access - primary_answer = choices[0].get("message", {}).get("content", "") if choices else "" - return { - "status": "success", - "answer": primary_answer, - "completion_id": data.get("id"), - } - except Exception as e: - return {"status": "error", "message": f"Failed to get RAG completion: {e!s}"} - - -@with_sync -async def accept_completion(completion_id: str) -> dict[str, Any]: - """ - Accept a suggestion generated by the Qiskit Code Assistant. - - Args: - completion_id: The ID of the completion to accept - - Returns: - Completion acceptance status - """ - try: - url = f"{QCA_TOOL_API_BASE}/v1/completion/acceptance" - body = {"completion": completion_id, "accepted": "true"} - data = await make_qca_request(url, method="POST", body=body) - - if "error" in data: - return {"status": "error", "message": data["error"]} - - result = data.get("result") - if not result: - return { - "status": "error", - "message": "No result for this completion acceptance.", - } - else: - return {"status": "success", "result": result} - except Exception as e: - return {"status": "error", "message": f"Failed to accept completion: {e!s}"} - - -@with_sync -async def get_service_status() -> str: - """ - Get current Qiskit Code Assistant service status. - - Returns: - Service connection status and basic information - """ - try: - logger.info("Checking Qiskit Code Assistant service status") - # Try to get models to test connectivity - models_result = await list_models() - - if models_result.get("status") == "success": - model_count = len(models_result.get("models", [])) - status_info = { - "connected": True, - "api_base": QCA_TOOL_API_BASE, - "model_name": QCA_TOOL_MODEL_NAME, - "available_models": model_count, - "timeout": QCA_REQUEST_TIMEOUT, - } - logger.info("Qiskit Code Assistant service is accessible") - else: - status_info = { - "connected": False, - "api_base": QCA_TOOL_API_BASE, - "model_name": QCA_TOOL_MODEL_NAME, - "error": models_result.get("message", "Unknown error"), - "timeout": QCA_REQUEST_TIMEOUT, - } - logger.warning("Qiskit Code Assistant service is not accessible") - - return f"Qiskit Code Assistant Service Status: {status_info}" - except Exception as e: - logger.error(f"Failed to check service status: {e!s}") - return f"Qiskit Code Assistant Service Status: Error - {e!s}" - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/server.py b/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/server.py deleted file mode 100644 index 2597476..0000000 --- a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/server.py +++ /dev/null @@ -1,303 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Qiskit Code Assistant MCP Server - -A Model Context Protocol server that provides access to IBM Qiskit Code Assistant -for intelligent quantum code completion and assistance. - -Dependencies: -- fastmcp -- httpx -- python-dotenv -""" - -import logging -from collections.abc import AsyncIterator -from contextlib import asynccontextmanager -from typing import Any - -import httpx -from fastmcp import FastMCP - -from qiskit_code_assistant_mcp_server.constants import ( - QCA_MCP_DEBUG_LEVEL, - QCA_REQUEST_TIMEOUT, - QCA_TOOL_X_CALLER, - validate_configuration, -) -from qiskit_code_assistant_mcp_server.qca import ( - accept_completion, - accept_model_disclaimer, - get_completion, - get_model, - get_model_disclaimer, - get_rag_completion, - get_service_status, - list_models, -) -from qiskit_code_assistant_mcp_server.utils import ( - _get_token, - clear_http_client, - set_http_client, -) - - -# Configure logging -logging.basicConfig(level=getattr(logging, QCA_MCP_DEBUG_LEVEL, logging.INFO)) -logger = logging.getLogger(__name__) - - -@asynccontextmanager -async def lifespan(server: FastMCP) -> AsyncIterator[None]: - """Manage the httpx client lifecycle.""" - headers = { - "x-caller": QCA_TOOL_X_CALLER, - "Accept": "application/json", - "Authorization": f"Bearer {_get_token()}", - } - async with httpx.AsyncClient( - headers=headers, - timeout=httpx.Timeout(QCA_REQUEST_TIMEOUT), - limits=httpx.Limits(max_keepalive_connections=5, max_connections=10), - ) as client: - set_http_client(client) - yield - clear_http_client() - - -# Initialize FastMCP server -mcp = FastMCP( - "Qiskit Code Assistant", - instructions="""\ -This server provides access to IBM Qiskit Code Assistant for quantum code \ -generation and conceptual Q&A. - -Recommended workflow: -1. Use get_completion_tool for CODE GENERATION requests (e.g., "write a Bell \ -state circuit", "create VQE code"). It returns ready-to-use Python/Qiskit code. -2. Use get_rag_completion_tool for CONCEPTUAL QUESTIONS (e.g., "what is \ -entanglement?", "how does the transpiler work?"). It retrieves answers from \ -IBM's knowledge base. -3. After a successful completion, call accept_completion_tool with the \ -completion_id to provide positive feedback and help improve the service. - -Disclaimer handling: -- Before first use, a model disclaimer may need to be accepted. -- Browse qca://disclaimer/{model_id} to check disclaimer status, then call \ -accept_model_disclaimer_tool to accept it. - -Browse qca://models to discover available models and qca://status to check \ -service health.\ -""", - lifespan=lifespan, -) - -logger.info("Qiskit Code Assistant MCP Server initialized") - -# Validate configuration on startup -if not validate_configuration(): - logger.error("Configuration validation failed - server may not work correctly") -else: - logger.info("Configuration validation passed") - - -################################################## -## MCP Resources -## - https://modelcontextprotocol.io/docs/concepts/resources -################################################## - - -@mcp.resource("qca://models", mime_type="application/json") -async def list_models_resource() -> dict[str, Any]: - """List the available models from the Qiskit Code Assistant.""" - return await list_models() - - -@mcp.resource("qca://model/{model_id}", mime_type="application/json") -async def get_model_resource(model_id: str) -> dict[str, Any]: - """Get the info for a specific model from the Qiskit Code Assistant. - - Args: - model_id: The ID of the model to retrieve - """ - return await get_model(model_id) - - -@mcp.resource("qca://disclaimer/{model_id}", mime_type="application/json") -async def get_model_disclaimer_resource(model_id: str) -> dict[str, Any]: - """Get the disclaimer for a specific model from the Qiskit Code Assistant. - - Args: - model_id: The ID of the model for which we want to retrieve the disclaimer - """ - return await get_model_disclaimer(model_id) - - -@mcp.resource("qca://status", mime_type="text/plain") -async def get_service_status_resource() -> str: - """Get current Qiskit Code Assistant service status.""" - return await get_service_status() - - -################################################## -## MCP Tools -## - https://modelcontextprotocol.io/docs/concepts/tools -################################################## - - -@mcp.tool() -async def accept_model_disclaimer_tool(model_id: str, disclaimer_id: str) -> dict[str, Any]: - """Accept the legal disclaimer for a Qiskit Code Assistant model. - - Call this when prompted with a disclaimer requirement before using completions. - The model_id and disclaimer_id are provided in the disclaimer prompt. - - Args: - model_id: The model ID (e.g., 'mistral-small-3.2-24b-qiskit') - disclaimer_id: The disclaimer ID from the disclaimer prompt - - Returns: - Dict with 'status' ('success' or 'error') and 'result' or 'message'. - """ - return await accept_model_disclaimer(model_id, disclaimer_id) - - -@mcp.tool() -async def get_completion_tool(prompt: str) -> dict[str, Any]: - """Generate Qiskit Python code based on a natural language prompt. - - Use this tool when users want to CREATE CODE for quantum circuits, algorithms, - or any Qiskit programming task. The tool returns ready-to-use Python code. - - When to use this tool: - - "Write a Bell state circuit" - - "Create a 3-qubit GHZ state" - - "Generate VQE code for H2 molecule" - - "Make a quantum random number generator" - - For conceptual questions (what/why/how explanations), use get_rag_completion_tool instead. - - Args: - prompt: Natural language description of the code to generate. - Be specific about circuit size, gates, and desired functionality. - - Returns: - Dict with: - - status: 'success' or 'error' - - code: The generated Python/Qiskit code (DISPLAY THIS TO THE USER) - - completion_id: ID for tracking (use with accept_completion_tool if user likes it) - - message: Error description if status is 'error' - - Example: prompt="Create a Bell state circuit" returns code like: - from qiskit import QuantumCircuit - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - """ - return await get_completion(prompt) - - -@mcp.tool() -async def get_rag_completion_tool(prompt: str) -> dict[str, Any]: - """Answer questions about Qiskit and quantum computing concepts using RAG. - - Use this tool when users ask CONCEPTUAL QUESTIONS that need explanations, - not code. The tool retrieves relevant information from IBM's knowledge base. - - When to use this tool: - - "What is quantum entanglement?" - - "How does the Qiskit transpiler work?" - - "Explain the difference between Sampler and Estimator" - - "What are the best practices for error mitigation?" - - For code generation requests, use get_completion_tool instead. - - Args: - prompt: A question about quantum computing or Qiskit concepts. - - Returns: - Dict with: - - status: 'success' or 'error' - - answer: The explanatory text response (DISPLAY THIS TO THE USER) - - completion_id: ID for tracking - - message: Error description if status is 'error' - """ - return await get_rag_completion(prompt) - - -@mcp.tool() -async def accept_completion_tool(completion_id: str) -> dict[str, Any]: - """Mark a generated completion as accepted/useful for feedback tracking. - - Call this after successfully using code or answers from get_completion_tool - or get_rag_completion_tool. This helps improve the Qiskit Code Assistant. - - Args: - completion_id: The completion_id from a previous completion response - - Returns: - Dict with 'status' ('success' or 'error') and 'result' or 'message'. - """ - return await accept_completion(completion_id) - - -################################################## -## MCP Prompts -## - https://modelcontextprotocol.io/docs/concepts/prompts -################################################## - - -@mcp.prompt() -def generate_qiskit_code(task: str) -> str: - """Generate Qiskit Python code for a quantum computing task.""" - return ( - f"Generate Qiskit code for: '{task}'. " - f"1) Call get_completion_tool with prompt='{task}' to generate the code, " - "2) Display the generated code from the response, " - "3) If the user confirms the code is useful, call accept_completion_tool " - "with the completion_id from the response." - ) - - -@mcp.prompt() -def explain_qiskit_concept(concept: str) -> str: - """Explain a Qiskit or quantum computing concept using the knowledge base.""" - return ( - f"Explain '{concept}' using the Qiskit Code Assistant knowledge base: " - f"Call get_rag_completion_tool with prompt='{concept}' and present the " - "explanation from the response. If the answer is helpful, call " - "accept_completion_tool with the completion_id." - ) - - -@mcp.prompt() -def setup_model(model_id: str) -> str: - """Set up a Qiskit Code Assistant model by reviewing its info and accepting the disclaimer.""" - return ( - f"Set up model '{model_id}' for use: " - f"1) Read the qca://model/{model_id} resource to get model details, " - f"2) Read the qca://disclaimer/{model_id} resource to get the disclaimer, " - "3) If a disclaimer is present, show it to the user and call " - f"accept_model_disclaimer_tool with model_id='{model_id}' and the " - "disclaimer_id from the response, " - "4) Confirm the model is ready for use." - ) - - -if __name__ == "__main__": - logger.info("Starting Qiskit Code Assistant MCP Server") - mcp.run(transport="stdio", show_banner=False) - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/utils.py b/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/utils.py deleted file mode 100644 index c7bf5c6..0000000 --- a/qiskit-code-assistant-mcp-server/src/qiskit_code_assistant_mcp_server/utils.py +++ /dev/null @@ -1,259 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Utility functions for the Qiskit Code Assistant MCP server. - -This module provides HTTP client utilities and the `with_sync` decorator for -creating dual async/sync APIs. - -Synchronous Execution ---------------------- -All async functions decorated with `@with_sync` can be called synchronously -via the `.sync` attribute: - - from qiskit_code_assistant_mcp_server.qca import list_models - - # Async usage (in async context) - result = await list_models() - - # Sync usage (in sync context, Jupyter notebooks, DSPy, etc.) - result = list_models.sync() - -The sync wrapper handles event loop management automatically, including -nested event loops in Jupyter notebooks (via nest_asyncio). -""" - -import asyncio -import json -import os -from collections.abc import Callable, Coroutine -from functools import wraps -from pathlib import Path -from typing import Any, TypeVar - -import httpx - -from qiskit_code_assistant_mcp_server.constants import ( - QCA_REQUEST_TIMEOUT, - QCA_TOOL_X_CALLER, -) - - -# Apply nest_asyncio to allow running async code in environments with existing event loops -try: - import nest_asyncio - - nest_asyncio.apply() -except ImportError: - pass - - -F = TypeVar("F", bound=Callable[..., Any]) -T = TypeVar("T") - - -def _run_async(coro: Coroutine[Any, Any, T]) -> T: - """Helper to run async functions synchronously. - - This handles both cases: - - Running in a Jupyter notebook or other environment with an existing event loop - - Running in a standard Python script without an event loop - """ - try: - loop = asyncio.get_event_loop() - if loop.is_running(): - # We're in a running loop (e.g., Jupyter), use run_until_complete - # This works because nest_asyncio allows nested loops - return loop.run_until_complete(coro) - else: - return loop.run_until_complete(coro) - except RuntimeError: - # No event loop exists, create one - return asyncio.run(coro) - - -def with_sync(func: F) -> F: - """Decorator that adds a `.sync` attribute to async functions for synchronous execution. - - Usage: - @with_sync - async def my_async_function(arg: str) -> Dict[str, Any]: - ... - - # Async call - result = await my_async_function("hello") - - # Sync call - result = my_async_function.sync("hello") - """ - - @wraps(func) - def sync_wrapper(*args: Any, **kwargs: Any) -> Any: - return _run_async(func(*args, **kwargs)) - - func.sync = sync_wrapper # type: ignore[attr-defined] - return func - - -def _get_token_from_system() -> str | None: - """Get the IBM Quantum token from environment or credentials file. - - Returns: - Token string if found, None otherwise. - """ - token = os.getenv("QISKIT_IBM_TOKEN") - - if not token: - qiskit_file = Path.home() / ".qiskit" / "qiskit-ibm.json" - if not qiskit_file.exists(): - return None - - try: - with open(qiskit_file) as _sc: - creds = json.loads(_sc.read()) - token = creds.get("default-ibm-quantum-platform", {}).get("token") - except Exception: - return None - - return token if token else None - - -# Lazy token retrieval - only fetch when first needed -_cached_token: str | None = None -_token_checked: bool = False - - -def _get_token() -> str: - """Get the IBM Quantum token, using cached value if available. - - Raises: - Exception: If no token is available when actually needed for an API call. - """ - global _cached_token, _token_checked - if not _token_checked: - _cached_token = _get_token_from_system() - _token_checked = True - if _cached_token is None: - raise Exception( - "No IBM Quantum token available. Please set env var QISKIT_IBM_TOKEN to access the service, " - "or save your IBM Quantum API token using QiskitRuntimeService. " - "More info: https://quantum.cloud.ibm.com/docs/en/api/qiskit-ibm-runtime/qiskit-runtime-service" - ) - return _cached_token - - -# Shared async client for better performance -_client: httpx.AsyncClient | None = None - - -def set_http_client(client: httpx.AsyncClient) -> None: - """Set the shared HTTP client (called by server lifespan).""" - global _client - _client = client - - -def clear_http_client() -> None: - """Clear the shared HTTP client (called on server shutdown).""" - global _client - _client = None - - -def get_http_client() -> httpx.AsyncClient: - """Get or create the shared HTTP client.""" - global _client - if _client is None or _client.is_closed: - headers = { - "x-caller": QCA_TOOL_X_CALLER, - "Accept": "application/json", - "Authorization": f"Bearer {_get_token()}", - } - _client = httpx.AsyncClient( - headers=headers, - timeout=httpx.Timeout(QCA_REQUEST_TIMEOUT), - limits=httpx.Limits(max_keepalive_connections=5, max_connections=10), - ) - return _client - - -async def close_http_client() -> None: - """Close the shared HTTP client.""" - global _client - if _client is not None and not _client.is_closed: - await _client.aclose() - _client = None - - -def get_error_message(response: httpx.Response) -> str: - msg = "Unable to fetch Qiskit Code Assistant or Qiskit Code Assistant failed" - - if not response.is_success: - try: - json_msg = response.json() | {} - msg = json_msg.get("detail", response.text) - - if response.status_code in [401, 403]: - msg = f"Qiskit Code Assistant API Token is not authorized or is incorrect: {msg}" - - except Exception: - msg = response.text - - return msg - - -async def make_qca_request( - url: str, - method: str, - params: dict[str, str] | None = None, - body: dict[str, Any] | None = None, - max_retries: int = 3, -) -> dict[str, Any]: - """Make an async request to the Qiskit Code Assistant with proper error handling and retry logic.""" - client = get_http_client() - last_exception: httpx.TimeoutException | httpx.ConnectError | Exception | None = None - response = None - - for attempt in range(max_retries): - try: - response = await client.request(method, url, params=params, json=body) - response.raise_for_status() - return dict(response.json()) - - except httpx.TimeoutException as e: - last_exception = e - if attempt < max_retries - 1: - wait_time = (attempt + 1) * 2 # Exponential backoff - await asyncio.sleep(wait_time) - continue - - except httpx.ConnectError as e: - last_exception = e - if attempt < max_retries - 1: - wait_time = (attempt + 1) * 2 - await asyncio.sleep(wait_time) - continue - - except httpx.HTTPStatusError as e: - # Don't retry on HTTP errors (4xx, 5xx) - return {"error": get_error_message(e.response)} - - except Exception as e: - last_exception = e - break - - # If we get here, all retries failed - if response is not None: - return {"error": get_error_message(response)} - else: - return {"error": f"Request failed after {max_retries} attempts: {last_exception!s}"} - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/tests/__init__.py b/qiskit-code-assistant-mcp-server/tests/__init__.py deleted file mode 100644 index 164c9fa..0000000 --- a/qiskit-code-assistant-mcp-server/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# Test package for Qiskit Code Assistant MCP Server diff --git a/qiskit-code-assistant-mcp-server/tests/conftest.py b/qiskit-code-assistant-mcp-server/tests/conftest.py deleted file mode 100644 index f1643fb..0000000 --- a/qiskit-code-assistant-mcp-server/tests/conftest.py +++ /dev/null @@ -1,269 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test configuration and fixtures for Qiskit Code Assistant MCP Server tests.""" - -import contextlib -import os -from unittest.mock import patch - -import httpx -import pytest -import respx - - -# Note: We cannot import from qiskit_code_assistant_mcp_server at module level -# because it triggers utils.py which calls _get_token_from_system() at import time. -# This would fail in CI where no token is available. Instead, we use hardcoded -# test values here and import lazily inside fixtures where needed. - -# Test constants (must match values set in mock_env_vars) -TEST_QCA_API_BASE = "https://test-qca-api.example.com" -TEST_QCA_MODEL_NAME = "test-model" - - -@pytest.fixture(autouse=True) -async def reset_http_client(): - """Reset the global HTTP client before and after each test. - - This fixture ensures clean state between tests and properly closes - any async client that was created during the test. - """ - # Import here to avoid triggering token lookup at module load time - import qiskit_code_assistant_mcp_server.utils as utils_module - - # Reset before test - force clear without trying to close - # (might already be closed or in invalid state) - utils_module.clear_http_client() - utils_module._cached_token = None # Reset cached token for fresh state - utils_module._token_checked = False # Reset token check flag - - yield - - # Reset after test - properly close if still open - with contextlib.suppress(Exception): - await utils_module.close_http_client() - utils_module.clear_http_client() # Ensure cleared even if close failed - utils_module._cached_token = None # Reset cached token after test - utils_module._token_checked = False # Reset token check flag after test - - -@pytest.fixture -async def http_client_for_tests(mock_env_vars): - """Create an HTTP client in proper async context for tests. - - This fixture pre-creates the AsyncClient in the async context, - avoiding sniffio detection issues in Python 3.14+. - Use this fixture for tests that need to make actual HTTP requests - (with respx mocking). - """ - # Import here after mock_env_vars has set up the environment - import qiskit_code_assistant_mcp_server.utils as utils_module - - # Use test constants directly (matching mock_env_vars values) - test_timeout = 10.0 - test_x_caller = "qiskit-mcp-server" - - # Get token from env (set by mock_env_vars) - token = os.environ.get("QISKIT_IBM_TOKEN", "test_token") - - headers = { - "x-caller": test_x_caller, - "Accept": "application/json", - "Authorization": f"Bearer {token}", - } - client = httpx.AsyncClient( - headers=headers, - timeout=httpx.Timeout(test_timeout), - limits=httpx.Limits(max_keepalive_connections=5, max_connections=10), - ) - utils_module.set_http_client(client) - - yield client - - # Cleanup is handled by reset_http_client autouse fixture - - -@pytest.fixture -def mock_env_vars(): - """Mock environment variables for testing. - - This fixture patches both environment variables AND the constants - in qca.py where they're already imported (since constants are - read at module import time). - """ - with patch.dict( - os.environ, - { - "QISKIT_IBM_TOKEN": "test_token_12345", - "QCA_TOOL_API_BASE": TEST_QCA_API_BASE, - "QCA_TOOL_MODEL_NAME": TEST_QCA_MODEL_NAME, - "QCA_REQUEST_TIMEOUT": "10.0", - "QCA_MCP_DEBUG_LEVEL": "DEBUG", - }, - ): - # Also patch the constants in qca.py where they've already been imported - with patch("qiskit_code_assistant_mcp_server.qca.QCA_TOOL_API_BASE", TEST_QCA_API_BASE): - with patch( - "qiskit_code_assistant_mcp_server.qca.QCA_TOOL_MODEL_NAME", TEST_QCA_MODEL_NAME - ): - yield - - -@pytest.fixture -def mock_qiskit_credentials(tmp_path): - """Mock Qiskit credentials file.""" - qiskit_dir = tmp_path / ".qiskit" - qiskit_dir.mkdir() - - credentials = { - "default-ibm-quantum-platform": { - "token": "test_token_from_file", - "url": "https://auth.quantum-computing.ibm.com/api", - } - } - - import json - - credentials_file = qiskit_dir / "qiskit-ibm.json" - with open(credentials_file, "w") as f: - json.dump(credentials, f) - - with patch("pathlib.Path.home", return_value=tmp_path): - yield credentials_file - - -@pytest.fixture -def mock_http_responses(mock_env_vars): - """Mock HTTP responses for QCA API calls. - - Note: This fixture depends on mock_env_vars to ensure constants are patched. - """ - with respx.mock(assert_all_called=False) as respx_mock: - # Mock models list endpoint - respx_mock.get(f"{TEST_QCA_API_BASE}/v1/models").mock( - return_value=httpx.Response( - 200, - json={ - "data": [ - { - "id": "mistral-small-3.2-24b-qiskit", - "name": "Mistral small 3.2 24b Qiskit", - }, - {"id": "test-model", "name": "Test Model"}, - ] - }, - ) - ) - - # Mock model details endpoint - respx_mock.get(f"{TEST_QCA_API_BASE}/v1/models/mistral-small-3.2-24b-qiskit").mock( - return_value=httpx.Response( - 200, - json={ - "id": "mistral-small-3.2-24b-qiskit", - "name": "Mistral small 3.2 24b Qiskit", - "description": "Test model for quantum code assistance", - }, - ) - ) - - # Mock model disclaimer endpoint - respx_mock.get( - f"{TEST_QCA_API_BASE}/v1/models/mistral-small-3.2-24b-qiskit/disclaimer" - ).mock( - return_value=httpx.Response( - 200, - json={ - "id": "disclaimer_123", - "model_id": "mistral-small-3.2-24b-qiskit", - "text": "This is a test disclaimer", - }, - ) - ) - - # Mock completion endpoint - respx_mock.post(f"{TEST_QCA_API_BASE}/v1/chat/completions").mock( - return_value=httpx.Response( - 200, - json={ - "id": "completion_456", - "choices": [ - { - "message": { - "role": "assistant", - "content": "# Create a quantum circuit\nqc = QuantumCircuit(2, 2)", - }, - "index": 0, - } - ], - }, - ) - ) - - # Mock disclaimer acceptance endpoint - respx_mock.post( - f"{TEST_QCA_API_BASE}/v1/models/mistral-small-3.2-24b-qiskit/disclaimer" - ).mock(return_value=httpx.Response(200, json={"success": True})) - - # Mock completion acceptance endpoint - respx_mock.post(f"{TEST_QCA_API_BASE}/v1/completion/acceptance").mock( - return_value=httpx.Response(200, json={"result": "accepted"}) - ) - - yield respx_mock - - -@pytest.fixture -def mock_http_error_responses(mock_env_vars): - """Mock HTTP error responses for testing error handling. - - Note: This fixture depends on mock_env_vars to ensure constants are patched. - """ - with respx.mock(assert_all_called=False) as respx_mock: - # Mock 401 Unauthorized - respx_mock.get(f"{TEST_QCA_API_BASE}/v1/models").mock( - return_value=httpx.Response(401, json={"detail": "Invalid authentication credentials"}) - ) - - # Mock 500 Server Error - respx_mock.post(f"{TEST_QCA_API_BASE}/v1/chat/completions").mock( - return_value=httpx.Response(500, json={"detail": "Internal server error"}) - ) - - yield respx_mock - - -@pytest.fixture -def sample_completion_request(mock_env_vars): - """Sample completion request data.""" - return { - "prompt": "Create a quantum circuit with 2 qubits", - "model": TEST_QCA_MODEL_NAME, - } - - -@pytest.fixture -def sample_models_response(): - """Sample models API response.""" - return { - "data": [ - { - "id": "mistral-small-3.2-24b-qiskit", - "name": "Mistral Small 3.2 24b Qiskit", - }, - {"id": "granite-3.3-8b-qiskit", "name": "Granite 8B Qiskit"}, - ] - } - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/tests/test_constants.py b/qiskit-code-assistant-mcp-server/tests/test_constants.py deleted file mode 100644 index 7bd981c..0000000 --- a/qiskit-code-assistant-mcp-server/tests/test_constants.py +++ /dev/null @@ -1,212 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Unit tests for constants and configuration.""" - -import os -from unittest.mock import patch - -from qiskit_code_assistant_mcp_server.constants import validate_configuration - - -class TestValidateConfiguration: - """Test configuration validation function.""" - - def test_validate_configuration_success(self, mock_env_vars): - """Test successful configuration validation.""" - with ( - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_API_BASE", - "https://valid-api.example.com", - ), - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_MODEL_NAME", - "valid-model", - ), - ): - result = validate_configuration() - assert result is True - - def test_validate_configuration_invalid_api_base(self): - """Test validation with invalid API base URL.""" - with ( - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_API_BASE", - "invalid-url", - ), - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_MODEL_NAME", - "valid-model", - ), - ): - result = validate_configuration() - assert result is False - - def test_validate_configuration_empty_model_name(self): - """Test validation with empty model name.""" - with ( - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_API_BASE", - "https://valid-api.example.com", - ), - patch("qiskit_code_assistant_mcp_server.constants.QCA_TOOL_MODEL_NAME", ""), - ): - result = validate_configuration() - assert result is False - - def test_validate_configuration_http_api_base(self): - """Test validation with HTTP API base (should be valid).""" - with ( - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_API_BASE", - "http://test-api.example.com", - ), - patch( - "qiskit_code_assistant_mcp_server.constants.QCA_TOOL_MODEL_NAME", - "valid-model", - ), - ): - result = validate_configuration() - assert result is True - - -class TestEnvironmentVariableHandling: - """Test environment variable processing.""" - - def test_request_timeout_valid(self): - """Test valid request timeout configuration.""" - with patch.dict(os.environ, {"QCA_REQUEST_TIMEOUT": "45.5"}): - # Re-import to get updated value - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert constants_module.QCA_REQUEST_TIMEOUT == 45.5 - - def test_request_timeout_invalid_negative(self): - """Test invalid negative timeout (should use default).""" - with patch.dict(os.environ, {"QCA_REQUEST_TIMEOUT": "-5.0"}): - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert constants_module.QCA_REQUEST_TIMEOUT == 30.0 - - def test_request_timeout_invalid_too_large(self): - """Test invalid too large timeout (should use default).""" - with patch.dict(os.environ, {"QCA_REQUEST_TIMEOUT": "400.0"}): - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert constants_module.QCA_REQUEST_TIMEOUT == 30.0 - - def test_debug_level_valid(self): - """Test valid debug level configuration.""" - with patch.dict(os.environ, {"QCA_MCP_DEBUG_LEVEL": "WARNING"}): - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert constants_module.QCA_MCP_DEBUG_LEVEL == "WARNING" - - def test_debug_level_invalid(self): - """Test invalid debug level (should use default).""" - with patch.dict(os.environ, {"QCA_MCP_DEBUG_LEVEL": "INVALID_LEVEL"}): - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert constants_module.QCA_MCP_DEBUG_LEVEL == "INFO" - - def test_api_base_trailing_slash_removal(self): - """Test that trailing slashes are removed from API base.""" - with patch.dict(os.environ, {"QCA_TOOL_API_BASE": "https://api.example.com/"}): - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert not constants_module.QCA_TOOL_API_BASE.endswith("/") - - def test_default_values(self): - """Test that default values are used when env vars are not set.""" - env_vars_to_remove = [ - "QCA_TOOL_API_BASE", - "QCA_TOOL_MODEL_NAME", - "QCA_REQUEST_TIMEOUT", - "QCA_MCP_DEBUG_LEVEL", - ] - - with patch.dict(os.environ, {}, clear=True): - # Remove any existing env vars - for var in env_vars_to_remove: - if var in os.environ: - del os.environ[var] - - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert ( - constants_module.QCA_TOOL_API_BASE - == "https://qiskit-code-assistant.quantum.ibm.com" - ) - assert constants_module.QCA_TOOL_MODEL_NAME == "mistral-small-3.2-24b-qiskit" - assert constants_module.QCA_REQUEST_TIMEOUT == 30.0 - assert constants_module.QCA_MCP_DEBUG_LEVEL == "INFO" - - -class TestVersionHandling: - """Test package version handling.""" - - def test_version_detection_success(self): - """Test successful version detection.""" - with patch("importlib.metadata.version") as mock_version: - mock_version.return_value = "1.2.3" - - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert "qiskit-code-assistant-mcp-server/1.2.3" in constants_module.QCA_TOOL_X_CALLER - - def test_version_detection_failure(self): - """Test version detection failure (should use unknown).""" - with patch("importlib.metadata.version") as mock_version: - mock_version.side_effect = Exception("Package not found") - - import importlib - - import qiskit_code_assistant_mcp_server.constants as constants_module - - importlib.reload(constants_module) - - assert "qiskit-code-assistant-mcp-server/unknown" in constants_module.QCA_TOOL_X_CALLER - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/tests/test_integration.py b/qiskit-code-assistant-mcp-server/tests/test_integration.py deleted file mode 100644 index ae2e271..0000000 --- a/qiskit-code-assistant-mcp-server/tests/test_integration.py +++ /dev/null @@ -1,284 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Integration tests for Qiskit Code Assistant MCP Server.""" - -from unittest.mock import patch - -import httpx -import pytest -import respx - -from qiskit_code_assistant_mcp_server.server import mcp - - -# Use the test API base URL that matches mock_env_vars fixture -TEST_QCA_API_BASE = "https://test-qca-api.example.com" - - -class TestMCPServerIntegration: - """Test MCP server integration.""" - - @pytest.mark.asyncio - async def test_server_initialization(self, mock_env_vars): - """Test that server initializes correctly.""" - # Server should initialize without errors - assert mcp is not None - assert mcp.name == "Qiskit Code Assistant" - - @pytest.mark.asyncio - async def test_server_instructions(self, mock_env_vars): - """Test the MCP server has instructions set.""" - assert mcp.instructions is not None - assert isinstance(mcp.instructions, str) - assert len(mcp.instructions) > 0 - # Verify instructions mention key workflow concepts - assert "get_completion_tool" in mcp.instructions - assert "get_rag_completion_tool" in mcp.instructions - assert "accept_completion_tool" in mcp.instructions - assert "qca://" in mcp.instructions - - @pytest.mark.asyncio - async def test_configuration_validation(self, mock_env_vars): - """Test that configuration validation runs on startup.""" - with patch( - "qiskit_code_assistant_mcp_server.constants.validate_configuration" - ) as mock_validate: - mock_validate.return_value = True - - # Reimport server module to trigger validation - import importlib - - import qiskit_code_assistant_mcp_server.server - - importlib.reload(qiskit_code_assistant_mcp_server.server) - - mock_validate.assert_called_once() - - -class TestResourceHandlers: - """Test MCP resource handlers.""" - - @pytest.mark.asyncio - async def test_models_resource(self, mock_env_vars, mock_http_responses): - """Test qca://models resource handler.""" - from qiskit_code_assistant_mcp_server.qca import list_models - - result = await list_models() - - assert result["status"] == "success" - assert "models" in result - assert len(result["models"]) > 0 - - @pytest.mark.asyncio - async def test_model_resource(self, mock_env_vars, mock_http_responses): - """Test qca://model/{model_id} resource handler.""" - from qiskit_code_assistant_mcp_server.qca import get_model - - result = await get_model("mistral-small-3.2-24b-qiskit") - - assert result["status"] == "success" - assert "model" in result - assert result["model"]["id"] == "mistral-small-3.2-24b-qiskit" - - @pytest.mark.asyncio - async def test_disclaimer_resource(self, mock_env_vars, mock_http_responses): - """Test qca://disclaimer/{model_id} resource handler.""" - from qiskit_code_assistant_mcp_server.qca import get_model_disclaimer - - result = await get_model_disclaimer("mistral-small-3.2-24b-qiskit") - - assert result["status"] == "success" - assert "disclaimer" in result - - @pytest.mark.asyncio - async def test_status_resource(self, mock_env_vars, mock_http_responses): - """Test qca://status resource handler.""" - from qiskit_code_assistant_mcp_server.qca import get_service_status - - result = await get_service_status() - - assert "Qiskit Code Assistant Service Status" in result - - -class TestToolHandlers: - """Test MCP tool handlers.""" - - @pytest.mark.asyncio - async def test_completion_tool(self, mock_env_vars, mock_http_responses): - """Test get_completion tool.""" - from qiskit_code_assistant_mcp_server.qca import get_completion - - result = await get_completion("Create a quantum circuit") - - assert result["status"] == "success" - assert "completion_id" in result - assert "code" in result - - @pytest.mark.asyncio - async def test_rag_completion_tool(self, mock_env_vars, mock_http_responses): - """Test get_rag_completion tool.""" - from qiskit_code_assistant_mcp_server.qca import get_rag_completion - - result = await get_rag_completion("What is quantum entanglement?") - - assert result["status"] == "success" - assert "completion_id" in result - assert "answer" in result - - @pytest.mark.asyncio - async def test_accept_disclaimer_tool(self, mock_env_vars, mock_http_responses): - """Test accept_model_disclaimer tool.""" - from qiskit_code_assistant_mcp_server.qca import accept_model_disclaimer - - result = await accept_model_disclaimer("mistral-small-3.2-24b-qiskit", "disclaimer_123") - - assert result["status"] == "success" - assert "result" in result - - @pytest.mark.asyncio - async def test_accept_completion_tool(self, mock_env_vars, mock_http_responses): - """Test accept_completion tool.""" - from qiskit_code_assistant_mcp_server.qca import accept_completion - - result = await accept_completion("completion_456") - - assert result["status"] == "success" - assert "result" in result - - -class TestErrorHandling: - """Test error handling in integration scenarios.""" - - @pytest.mark.asyncio - async def test_network_timeout_handling(self, mock_env_vars): - """Test handling of network timeouts.""" - with respx.mock() as respx_mock: - respx_mock.get(f"{TEST_QCA_API_BASE}/v1/models").mock( - side_effect=httpx.TimeoutException("Request timeout") - ) - - from qiskit_code_assistant_mcp_server.qca import list_models - - result = await list_models() - - assert result["status"] == "error" - assert "timeout" in result["message"].lower() or "failed" in result["message"].lower() - - @pytest.mark.asyncio - async def test_authentication_error_handling(self, mock_env_vars): - """Test handling of authentication errors.""" - with respx.mock() as respx_mock: - respx_mock.get(f"{TEST_QCA_API_BASE}/v1/models").mock( - return_value=httpx.Response(401, json={"detail": "Invalid token"}) - ) - - from qiskit_code_assistant_mcp_server.qca import list_models - - result = await list_models() - - assert result["status"] == "error" - - @pytest.mark.asyncio - async def test_server_error_handling(self, mock_env_vars): - """Test handling of server errors.""" - with respx.mock() as respx_mock: - respx_mock.post(f"{TEST_QCA_API_BASE}/v1/chat/completions").mock( - return_value=httpx.Response(500, json={"detail": "Internal server error"}) - ) - - from qiskit_code_assistant_mcp_server.qca import get_completion - - result = await get_completion("test prompt") - - assert result["status"] == "error" - - -class TestEndToEndScenarios: - """Test end-to-end scenarios.""" - - @pytest.mark.asyncio - async def test_complete_workflow(self, mock_env_vars, mock_http_responses): - """Test complete workflow: list models -> get completion -> accept.""" - from qiskit_code_assistant_mcp_server.qca import ( - accept_completion, - get_completion, - list_models, - ) - - # 1. List models - models_result = await list_models() - assert models_result["status"] == "success" - - # 2. Get completion - completion_result = await get_completion("Create a quantum circuit") - assert completion_result["status"] == "success" - completion_id = completion_result["completion_id"] - - # 3. Accept completion - accept_result = await accept_completion(completion_id) - assert accept_result["status"] == "success" - - @pytest.mark.asyncio - async def test_disclaimer_workflow(self, mock_env_vars, mock_http_responses): - """Test disclaimer workflow: get disclaimer -> accept disclaimer.""" - from qiskit_code_assistant_mcp_server.qca import ( - accept_model_disclaimer, - get_model_disclaimer, - ) - - # 1. Get disclaimer - disclaimer_result = await get_model_disclaimer("mistral-small-3.2-24b-qiskit") - assert disclaimer_result["status"] == "success" - disclaimer_id = disclaimer_result["disclaimer"]["id"] - - # 2. Accept disclaimer - accept_result = await accept_model_disclaimer("mistral-small-3.2-24b-qiskit", disclaimer_id) - assert accept_result["status"] == "success" - - -class TestServerRegistration: - """Test that prompts and resource templates are registered.""" - - def test_prompts_registered(self): - """Test that all expected prompts are registered.""" - prompt_names = set(mcp._prompt_manager._prompts.keys()) - expected_prompts = { - "generate_qiskit_code", - "explain_qiskit_concept", - "setup_model", - } - assert expected_prompts.issubset(prompt_names), ( - f"Missing prompts: {expected_prompts - prompt_names}" - ) - - def test_prompt_count(self): - """Test the expected number of prompts.""" - assert len(mcp._prompt_manager._prompts) == 3 - - def test_resource_templates_registered(self): - """Test that all expected resource templates are registered.""" - template_uris = set(mcp._resource_manager._templates.keys()) - expected_templates = { - "qca://model/{model_id}", - "qca://disclaimer/{model_id}", - } - assert expected_templates.issubset(template_uris), ( - f"Missing resource templates: {expected_templates - template_uris}" - ) - - def test_resource_template_count(self): - """Test the expected number of resource templates.""" - assert len(mcp._resource_manager._templates) == 2 - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/tests/test_qca.py b/qiskit-code-assistant-mcp-server/tests/test_qca.py deleted file mode 100644 index f5cd820..0000000 --- a/qiskit-code-assistant-mcp-server/tests/test_qca.py +++ /dev/null @@ -1,371 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Unit tests for QCA functions.""" - -from unittest.mock import patch - -import pytest - -from qiskit_code_assistant_mcp_server.qca import ( - accept_completion, - accept_model_disclaimer, - get_completion, - get_model, - get_rag_completion, - get_service_status, - list_models, -) - - -class TestListModels: - """Test list_models function.""" - - @pytest.mark.asyncio - async def test_list_models_success(self, mock_env_vars, mock_http_responses): - """Test successful models listing.""" - result = await list_models() - - assert result["status"] == "success" - assert "models" in result - assert len(result["models"]) == 2 - assert result["models"][0]["id"] == "mistral-small-3.2-24b-qiskit" - - @pytest.mark.asyncio - async def test_list_models_empty_response(self, mock_env_vars): - """Test handling of empty models response.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.return_value = {"data": []} - - result = await list_models() - - assert result["status"] == "error" - assert "No models retrieved" in result["message"] - - @pytest.mark.asyncio - async def test_list_models_api_error(self, mock_env_vars): - """Test handling of API error.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.return_value = {"error": "Authentication failed"} - - result = await list_models() - - assert result["status"] == "error" - assert result["message"] == "Authentication failed" - - @pytest.mark.asyncio - async def test_list_models_exception(self, mock_env_vars): - """Test handling of unexpected exceptions.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.side_effect = Exception("Network error") - - result = await list_models() - - assert result["status"] == "error" - assert "Failed to list models" in result["message"] - - -class TestGetModel: - """Test get_model function.""" - - @pytest.mark.asyncio - async def test_get_model_success(self, mock_env_vars, mock_http_responses): - """Test successful model retrieval.""" - result = await get_model("mistral-small-3.2-24b-qiskit") - - assert result["status"] == "success" - assert "model" in result - assert result["model"]["id"] == "mistral-small-3.2-24b-qiskit" - - @pytest.mark.asyncio - async def test_get_model_empty_id(self, mock_env_vars): - """Test validation of empty model ID.""" - result = await get_model("") - - assert result["status"] == "error" - assert "model_id is required" in result["message"] - - @pytest.mark.asyncio - async def test_get_model_whitespace_id(self, mock_env_vars): - """Test validation of whitespace-only model ID.""" - result = await get_model(" ") - - assert result["status"] == "error" - assert "model_id is required" in result["message"] - - @pytest.mark.asyncio - async def test_get_model_not_found(self, mock_env_vars): - """Test handling of model not found.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.return_value = {"name": "Model X"} # Missing 'id' field - - result = await get_model("nonexistent-model") - - assert result["status"] == "error" - assert "Model not retrieved" in result["message"] - - -class TestGetCompletion: - """Test get_completion function.""" - - @pytest.mark.asyncio - async def test_get_completion_success(self, mock_env_vars, mock_http_responses): - """Test successful completion generation.""" - prompt = "Create a quantum circuit with 2 qubits" - result = await get_completion(prompt) - - assert result["status"] == "success" - assert "completion_id" in result - assert "code" in result - assert "quantum circuit" in result["code"].lower() - - @pytest.mark.asyncio - async def test_get_completion_empty_prompt(self, mock_env_vars): - """Test validation of empty prompt.""" - result = await get_completion("") - - assert result["status"] == "error" - assert "prompt is required" in result["message"] - - @pytest.mark.asyncio - async def test_get_completion_whitespace_prompt(self, mock_env_vars): - """Test validation of whitespace-only prompt.""" - result = await get_completion(" ") - - assert result["status"] == "error" - assert "prompt is required" in result["message"] - - @pytest.mark.asyncio - async def test_get_completion_too_long_prompt(self, mock_env_vars): - """Test validation of overly long prompt.""" - long_prompt = "x" * 10001 # Exceeds 10000 char limit - result = await get_completion(long_prompt) - - assert result["status"] == "error" - assert "prompt is too long" in result["message"] - - @pytest.mark.asyncio - async def test_get_completion_no_choices(self, mock_env_vars): - """Test handling of response with no choices.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.return_value = {"id": "completion_123", "choices": []} - - result = await get_completion("test prompt") - - assert result["status"] == "error" - assert "No choices for this prompt" in result["message"] - - -class TestGetRAGCompletion: - """Test get_rag_completion function.""" - - @pytest.mark.asyncio - async def test_get_rag_completion_success(self, mock_env_vars, mock_http_responses): - """Test successful RAG completion generation.""" - prompt = "What is quantum entanglement?" - result = await get_rag_completion(prompt) - - assert result["status"] == "success" - assert "completion_id" in result - assert "answer" in result - - -class TestAcceptModelDisclaimer: - """Test accept_model_disclaimer function.""" - - @pytest.mark.asyncio - async def test_accept_disclaimer_success(self, mock_env_vars, mock_http_responses): - """Test successful disclaimer acceptance.""" - result = await accept_model_disclaimer("mistral-small-3.2-24b-qiskit", "disclaimer_123") - - assert result["status"] == "success" - assert "result" in result - - @pytest.mark.asyncio - async def test_accept_disclaimer_no_success_field(self, mock_env_vars): - """Test handling of response without success field.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.return_value = {"acknowledged": True} # Missing 'success' field - - result = await accept_model_disclaimer("mistral-small-3.2-24b-qiskit", "disclaimer_123") - - assert result["status"] == "error" - assert "acceptance result" in result["message"] - - -class TestAcceptCompletion: - """Test accept_completion function.""" - - @pytest.mark.asyncio - async def test_accept_completion_success(self, mock_env_vars, mock_http_responses): - """Test successful completion acceptance.""" - result = await accept_completion("completion_456") - - assert result["status"] == "success" - assert "result" in result - - @pytest.mark.asyncio - async def test_accept_completion_no_result(self, mock_env_vars): - """Test handling of response without result field.""" - with patch("qiskit_code_assistant_mcp_server.qca.make_qca_request") as mock_request: - mock_request.return_value = {"status": "ok"} # Missing 'result' field - - result = await accept_completion("completion_456") - - assert result["status"] == "error" - assert "No result for this completion acceptance" in result["message"] - - -class TestServiceStatus: - """Test get_service_status function.""" - - @pytest.mark.asyncio - async def test_service_status_connected(self, mock_env_vars, mock_http_responses): - """Test service status when connected.""" - result = await get_service_status() - - assert "Qiskit Code Assistant Service Status" in result - assert "connected" in result.lower() - - @pytest.mark.asyncio - async def test_service_status_disconnected(self, mock_env_vars): - """Test service status when disconnected.""" - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list: - mock_list.return_value = {"status": "error", "message": "Connection failed"} - - result = await get_service_status() - - assert "Qiskit Code Assistant Service Status" in result - assert "connected" in result.lower() - - @pytest.mark.asyncio - async def test_service_status_exception(self, mock_env_vars): - """Test service status with exception.""" - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list: - mock_list.side_effect = Exception("Network error") - - result = await get_service_status() - - assert "Error" in result - - -class TestModelSelection: - """Test _select_available_model function.""" - - def test_model_selection_default_available(self, mock_env_vars): - """Test that default model is selected when available.""" - from qiskit_code_assistant_mcp_server.constants import QCA_TOOL_MODEL_NAME - from qiskit_code_assistant_mcp_server.qca import _select_available_model - - # Mock list_models to return models including the default - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list_models: - mock_list_models.return_value = { - "status": "success", - "models": [ - {"id": QCA_TOOL_MODEL_NAME, "name": "Default Model"}, - {"id": "another-model", "name": "Another Model"}, - ], - } - - result = _select_available_model() - - assert result == QCA_TOOL_MODEL_NAME - - def test_model_selection_default_unavailable(self, mock_env_vars): - """Test that first available model is selected when default is not available.""" - from qiskit_code_assistant_mcp_server.constants import QCA_TOOL_MODEL_NAME - from qiskit_code_assistant_mcp_server.qca import _select_available_model - - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list_models: - # Return models that don't include the default - mock_list_models.return_value = { - "status": "success", - "models": [ - {"id": "granite-3.3-8b-qiskit", "name": "Granite 8B Qiskit"}, - {"id": "other-model", "name": "Other Model"}, - ], - } - - result = _select_available_model() - - # Should select first available model since default is not in the list - assert result == "granite-3.3-8b-qiskit" - assert result != QCA_TOOL_MODEL_NAME - - def test_model_selection_no_models_available(self, mock_env_vars): - """Test graceful handling when no models are available.""" - from qiskit_code_assistant_mcp_server.qca import _select_available_model - - # The expected model name matches what mock_env_vars patches - expected_model = "test-model" - - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list_models: - mock_list_models.return_value = {"status": "success", "models": []} - - result = _select_available_model() - - # Should fallback to configured default (patched to test-model by mock_env_vars) - assert result == expected_model - - def test_model_selection_api_error(self, mock_env_vars): - """Test graceful handling when API call fails.""" - from qiskit_code_assistant_mcp_server.qca import _select_available_model - - # The expected model name matches what mock_env_vars patches - expected_model = "test-model" - - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list_models: - mock_list_models.return_value = { - "status": "error", - "message": "Authentication failed", - } - - result = _select_available_model() - - # Should fallback to configured default (patched to test-model by mock_env_vars) - assert result == expected_model - - def test_model_selection_exception(self, mock_env_vars): - """Test graceful handling when an exception occurs.""" - from qiskit_code_assistant_mcp_server.qca import _select_available_model - - # The expected model name matches what mock_env_vars patches - expected_model = "test-model" - - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list_models: - mock_list_models.side_effect = Exception("Network error") - - result = _select_available_model() - - # Should fallback to configured default (patched to test-model by mock_env_vars) - assert result == expected_model - - def test_model_selection_models_without_ids(self, mock_env_vars): - """Test handling of models without IDs.""" - from qiskit_code_assistant_mcp_server.qca import _select_available_model - - with patch("qiskit_code_assistant_mcp_server.qca.list_models") as mock_list_models: - mock_list_models.return_value = { - "status": "success", - "models": [ - {"name": "Model without ID"}, # Missing 'id' field - {"id": "valid-model", "name": "Valid Model"}, - ], - } - - result = _select_available_model() - - # Should skip models without IDs and select the valid one - assert result == "valid-model" - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/tests/test_sync.py b/qiskit-code-assistant-mcp-server/tests/test_sync.py deleted file mode 100644 index d63acaa..0000000 --- a/qiskit-code-assistant-mcp-server/tests/test_sync.py +++ /dev/null @@ -1,168 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Unit tests for the with_sync decorator and .sync methods.""" - -from unittest.mock import patch - -from qiskit_code_assistant_mcp_server.qca import ( - accept_completion, - get_completion, - get_model, - get_rag_completion, - get_service_status, - list_models, -) - - -class TestWithSyncDecorator: - """Test that async functions have .sync attribute.""" - - def test_list_models_has_sync(self): - """Test list_models has .sync attribute.""" - assert hasattr(list_models, "sync") - assert callable(list_models.sync) - - def test_get_model_has_sync(self): - """Test get_model has .sync attribute.""" - assert hasattr(get_model, "sync") - assert callable(get_model.sync) - - def test_get_completion_has_sync(self): - """Test get_completion has .sync attribute.""" - assert hasattr(get_completion, "sync") - assert callable(get_completion.sync) - - def test_get_rag_completion_has_sync(self): - """Test get_rag_completion has .sync attribute.""" - assert hasattr(get_rag_completion, "sync") - assert callable(get_rag_completion.sync) - - def test_accept_completion_has_sync(self): - """Test accept_completion has .sync attribute.""" - assert hasattr(accept_completion, "sync") - assert callable(accept_completion.sync) - - def test_get_service_status_has_sync(self): - """Test get_service_status has .sync attribute.""" - assert hasattr(get_service_status, "sync") - assert callable(get_service_status.sync) - - -class TestSyncMethodExecution: - """Test that .sync methods execute correctly.""" - - def test_list_models_sync_success(self, mock_env_vars): - """Test successful models listing with .sync method.""" - mock_response = { - "status": "success", - "models": [ - {"id": "mistral-small-3.2-24b-qiskit", "name": "Granite Qiskit"}, - {"id": "granite-3.3-2b-qiskit", "name": "Granite Qiskit Small"}, - ], - } - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = list_models.sync() - - assert result["status"] == "success" - assert "models" in result - assert len(result["models"]) == 2 - - def test_list_models_sync_error(self, mock_env_vars): - """Test error handling in .sync method.""" - mock_response = {"status": "error", "message": "Authentication failed"} - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = list_models.sync() - - assert result["status"] == "error" - assert "Authentication failed" in result["message"] - - def test_get_model_sync_success(self, mock_env_vars): - """Test successful model retrieval with .sync method.""" - mock_response = { - "status": "success", - "model": {"id": "mistral-small-3.2-24b-qiskit", "name": "Granite Qiskit"}, - } - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = get_model.sync("mistral-small-3.2-24b-qiskit") - - assert result["status"] == "success" - assert result["model"]["id"] == "mistral-small-3.2-24b-qiskit" - - def test_get_completion_sync_success(self, mock_env_vars): - """Test successful code completion with .sync method.""" - mock_response = { - "status": "success", - "completion_id": "comp_123", - "choices": [ - {"message": {"role": "assistant", "content": "from qiskit import QuantumCircuit"}} - ], - } - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = get_completion.sync("Create a quantum circuit") - - assert result["status"] == "success" - assert "completion_id" in result - assert len(result["choices"]) > 0 - - def test_get_rag_completion_sync_success(self, mock_env_vars): - """Test successful RAG completion with .sync method.""" - mock_response = { - "status": "success", - "completion_id": "rag_123", - "choices": [ - {"message": {"role": "assistant", "content": "Quantum entanglement is..."}} - ], - } - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = get_rag_completion.sync("What is quantum entanglement?") - - assert result["status"] == "success" - assert "choices" in result - - def test_accept_completion_sync_success(self, mock_env_vars): - """Test successful completion acceptance with .sync method.""" - mock_response = {"status": "success", "result": "accepted"} - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = accept_completion.sync("comp_123") - - assert result["status"] == "success" - assert result["result"] == "accepted" - - def test_get_service_status_sync_success(self, mock_env_vars): - """Test successful service status check with .sync method.""" - mock_response = "Qiskit Code Assistant Service Status: {'connected': True}" - - with patch("qiskit_code_assistant_mcp_server.utils._run_async") as mock_run: - mock_run.return_value = mock_response - - result = get_service_status.sync() - - assert "connected" in result diff --git a/qiskit-code-assistant-mcp-server/tests/test_utils.py b/qiskit-code-assistant-mcp-server/tests/test_utils.py deleted file mode 100644 index 1d1ab52..0000000 --- a/qiskit-code-assistant-mcp-server/tests/test_utils.py +++ /dev/null @@ -1,348 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2025. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Unit tests for utility functions.""" - -import os -from unittest.mock import Mock, patch - -import httpx -import pytest -import respx - -from qiskit_code_assistant_mcp_server.utils import ( - clear_http_client, - close_http_client, - get_error_message, - get_http_client, - make_qca_request, - set_http_client, -) - - -class TestGetTokenFromSystem: - """Test token retrieval from system.""" - - def test_get_token_from_env(self, mock_env_vars): - """Test token retrieval from environment variable.""" - with patch.dict("os.environ", {"QISKIT_IBM_TOKEN": "env_token_123"}): - # Need to reload the module to get the updated token - from qiskit_code_assistant_mcp_server.utils import _get_token_from_system - - token = _get_token_from_system() - assert token == "env_token_123" - - def test_get_token_from_file(self, mock_qiskit_credentials): - """Test token retrieval from credentials file.""" - with patch.dict("os.environ", {}, clear=True): - # Remove QISKIT_IBM_TOKEN from env - if "QISKIT_IBM_TOKEN" in os.environ: - del os.environ["QISKIT_IBM_TOKEN"] - - from qiskit_code_assistant_mcp_server.utils import _get_token_from_system - - token = _get_token_from_system() - assert token == "test_token_from_file" - - def test_get_token_no_env_no_file(self): - """Test None returned when no token is available.""" - with patch.dict("os.environ", {}, clear=True): - with patch("pathlib.Path.exists", return_value=False): - from qiskit_code_assistant_mcp_server.utils import ( - _get_token_from_system, - ) - - token = _get_token_from_system() - assert token is None - - def test_get_token_file_missing_default(self, mock_qiskit_credentials): - """Test None returned when credentials file exists but missing default entry.""" - import json - - # Create file without default-ibm-quantum-platform entry - with open(mock_qiskit_credentials, "w") as f: - json.dump({"other-account": {"token": "other_token"}}, f) - - with patch.dict("os.environ", {}, clear=True): - from qiskit_code_assistant_mcp_server.utils import _get_token_from_system - - token = _get_token_from_system() - assert token is None - - def test_get_token_raises_when_no_token(self): - """Test _get_token raises exception when no token is available.""" - import qiskit_code_assistant_mcp_server.utils as utils_module - - # Reset state - utils_module._cached_token = None - utils_module._token_checked = False - - with patch.dict("os.environ", {}, clear=True): - with patch("pathlib.Path.exists", return_value=False): - with pytest.raises(Exception) as exc_info: - utils_module._get_token() - - assert "No IBM Quantum token available" in str(exc_info.value) - - -class TestGetErrorMessage: - """Test error message extraction from HTTP responses.""" - - def test_get_error_message_success_response(self): - """Test error message for successful response.""" - response = Mock(spec=httpx.Response) - response.is_success = True - - message = get_error_message(response) - assert "Unable to fetch Qiskit Code Assistant" in message - - def test_get_error_message_json_detail(self): - """Test error message extraction from JSON response.""" - response = Mock(spec=httpx.Response) - response.is_success = False - response.status_code = 401 - response.json.return_value = {"detail": "Invalid token"} - response.text = "Raw error text" - - message = get_error_message(response) - assert "Qiskit Code Assistant API Token is not authorized" in message - assert "Invalid token" in message - - def test_get_error_message_json_exception(self): - """Test error message when JSON parsing fails.""" - response = Mock(spec=httpx.Response) - response.is_success = False - response.status_code = 500 - response.json.side_effect = Exception("JSON decode error") - response.text = "Internal server error" - - message = get_error_message(response) - assert message == "Internal server error" - - def test_get_error_message_403_forbidden(self): - """Test error message for 403 Forbidden.""" - response = Mock(spec=httpx.Response) - response.is_success = False - response.status_code = 403 - response.json.return_value = {"detail": "Access denied"} - response.text = "Forbidden" - - message = get_error_message(response) - assert "Qiskit Code Assistant API Token is not authorized" in message - assert "Access denied" in message - - -class TestHTTPClient: - """Test HTTP client management.""" - - @pytest.fixture - def mock_async_client(self): - """Create a mock AsyncClient that doesn't require async context.""" - mock_client = Mock(spec=httpx.AsyncClient) - mock_client.is_closed = False - - async def mock_aclose(): - mock_client.is_closed = True - - mock_client.aclose = mock_aclose - return mock_client - - @pytest.mark.asyncio - async def test_get_http_client_creation(self, mock_env_vars, mock_async_client): - """Test HTTP client creation.""" - with patch.object(httpx, "AsyncClient", return_value=mock_async_client): - client = get_http_client() - assert client is mock_async_client - assert not client.is_closed - - @pytest.mark.asyncio - async def test_get_http_client_reuse(self, mock_env_vars, mock_async_client): - """Test HTTP client reuse.""" - with patch.object(httpx, "AsyncClient", return_value=mock_async_client): - client1 = get_http_client() - client2 = get_http_client() - - assert client1 is client2 # Same instance - - @pytest.mark.asyncio - async def test_close_http_client(self, mock_env_vars, mock_async_client): - """Test HTTP client closure.""" - with patch.object(httpx, "AsyncClient", return_value=mock_async_client): - client = get_http_client() - assert not client.is_closed - - await close_http_client() - assert client.is_closed - - @pytest.mark.asyncio - async def test_set_http_client(self, mock_async_client): - """Test setting the shared HTTP client.""" - set_http_client(mock_async_client) - client = get_http_client() - assert client is mock_async_client - - def test_clear_http_client(self, mock_env_vars, mock_async_client): - """Test clearing the shared HTTP client.""" - set_http_client(mock_async_client) - clear_http_client() - # After clearing, get_http_client should create a new instance - with patch.object(httpx, "AsyncClient", return_value=mock_async_client): - client = get_http_client() - assert client is mock_async_client - - @pytest.mark.asyncio - async def test_get_client_after_close(self, mock_env_vars): - """Test getting client after closure creates new instance.""" - mock_client1 = Mock(spec=httpx.AsyncClient) - mock_client1.is_closed = False - - async def mock_aclose1(): - mock_client1.is_closed = True - - mock_client1.aclose = mock_aclose1 - - mock_client2 = Mock(spec=httpx.AsyncClient) - mock_client2.is_closed = False - - with patch.object(httpx, "AsyncClient", side_effect=[mock_client1, mock_client2]): - client1 = get_http_client() - await close_http_client() - - client2 = get_http_client() - assert client1 is not client2 - assert client1.is_closed - assert not client2.is_closed - - -class TestMakeQCARequest: - """Test QCA API request function.""" - - @pytest.mark.asyncio - async def test_make_request_success(self, http_client_for_tests): - """Test successful API request.""" - with respx.mock() as respx_mock: - respx_mock.get("https://test-api.example.com/test").mock( - return_value=httpx.Response(200, json={"result": "success"}) - ) - - result = await make_qca_request("https://test-api.example.com/test", "GET") - - assert result == {"result": "success"} - - @pytest.mark.asyncio - async def test_make_request_with_params(self, http_client_for_tests): - """Test API request with parameters.""" - with respx.mock() as respx_mock: - respx_mock.get("https://test-api.example.com/test").mock( - return_value=httpx.Response(200, json={"result": "success"}) - ) - - result = await make_qca_request( - "https://test-api.example.com/test", "GET", params={"q": "test"} - ) - - assert result == {"result": "success"} - - @pytest.mark.asyncio - async def test_make_request_with_body(self, http_client_for_tests): - """Test API request with JSON body.""" - with respx.mock() as respx_mock: - respx_mock.post("https://test-api.example.com/test").mock( - return_value=httpx.Response(200, json={"result": "created"}) - ) - - result = await make_qca_request( - "https://test-api.example.com/test", "POST", body={"data": "test"} - ) - - assert result == {"result": "created"} - - @pytest.mark.asyncio - async def test_make_request_http_error(self, http_client_for_tests): - """Test API request with HTTP error.""" - with respx.mock() as respx_mock: - respx_mock.get("https://test-api.example.com/test").mock( - return_value=httpx.Response(404, json={"detail": "Not found"}) - ) - - result = await make_qca_request("https://test-api.example.com/test", "GET") - - assert "error" in result - assert "Not found" in result["error"] or "Unable to fetch" in result["error"] - - @pytest.mark.asyncio - async def test_make_request_timeout_retry(self, http_client_for_tests): - """Test API request with timeout and retry.""" - with respx.mock() as respx_mock: - # First two calls timeout, third succeeds - respx_mock.get("https://test-api.example.com/test").mock( - side_effect=[ - httpx.TimeoutException("Request timeout"), - httpx.TimeoutException("Request timeout"), - httpx.Response(200, json={"result": "success"}), - ] - ) - - result = await make_qca_request( - "https://test-api.example.com/test", "GET", max_retries=3 - ) - - assert result == {"result": "success"} - - @pytest.mark.asyncio - async def test_make_request_connection_error_retry(self, http_client_for_tests): - """Test API request with connection error and retry.""" - with respx.mock() as respx_mock: - # First call fails, second succeeds - respx_mock.get("https://test-api.example.com/test").mock( - side_effect=[ - httpx.ConnectError("Connection failed"), - httpx.Response(200, json={"result": "success"}), - ] - ) - - result = await make_qca_request( - "https://test-api.example.com/test", "GET", max_retries=2 - ) - - assert result == {"result": "success"} - - @pytest.mark.asyncio - async def test_make_request_max_retries_exceeded(self, http_client_for_tests): - """Test API request when max retries exceeded.""" - with respx.mock() as respx_mock: - respx_mock.get("https://test-api.example.com/test").mock( - side_effect=httpx.TimeoutException("Request timeout") - ) - - result = await make_qca_request( - "https://test-api.example.com/test", "GET", max_retries=2 - ) - - assert "error" in result - assert "Request failed after 2 attempts" in result["error"] - - @pytest.mark.asyncio - async def test_make_request_unexpected_exception(self, http_client_for_tests): - """Test API request with unexpected exception.""" - with respx.mock() as respx_mock: - respx_mock.get("https://test-api.example.com/test").mock( - side_effect=ValueError("Unexpected error") - ) - - result = await make_qca_request("https://test-api.example.com/test", "GET") - - assert "error" in result - assert "Request failed after" in result["error"] - - -# Assisted by watsonx Code Assistant diff --git a/qiskit-code-assistant-mcp-server/uv.lock b/qiskit-code-assistant-mcp-server/uv.lock deleted file mode 100644 index e29ef5b..0000000 --- a/qiskit-code-assistant-mcp-server/uv.lock +++ /dev/null @@ -1,1585 +0,0 @@ -version = 1 -revision = 2 -requires-python = ">=3.10, <4.0" - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, -] - -[[package]] -name = "anyio" -version = "4.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, -] - -[[package]] -name = "attrs" -version = "25.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/c6/d9a9db2e71957827e23a34322bde8091b51cb778dcc38885b84c772a1ba9/authlib-1.6.3.tar.gz", hash = "sha256:9f7a982cc395de719e4c2215c5707e7ea690ecf84f1ab126f28c053f4219e610", size = 160836, upload-time = "2025-08-26T12:13:25.206Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/2f/efa9d26dbb612b774990741fd8f13c7cf4cfd085b870e4a5af5c82eaf5f1/authlib-1.6.3-py2.py3-none-any.whl", hash = "sha256:7ea0f082edd95a03b7b72edac65ec7f8f68d703017d7e37573aee4fc603f2a48", size = 240105, upload-time = "2025-08-26T12:13:23.889Z" }, -] - -[[package]] -name = "backports-asyncio-runner" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, -] - -[[package]] -name = "certifi" -version = "2025.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, -] - -[[package]] -name = "cffi" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" }, - { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" }, - { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" }, - { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" }, - { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" }, - { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" }, - { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" }, - { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" }, - { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" }, - { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" }, - { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" }, - { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" }, - { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" }, - { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" }, - { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" }, - { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, - { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, - { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, - { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, - { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, - { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, - { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, - { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, - { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, - { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, - { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" }, - { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" }, - { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" }, - { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" }, - { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" }, - { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" }, - { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" }, - { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" }, - { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" }, - { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" }, - { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" }, - { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" }, - { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" }, - { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" }, - { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" }, - { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" }, - { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, -] - -[[package]] -name = "click" -version = "8.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "coverage" -version = "7.10.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/1d/2e64b43d978b5bd184e0756a41415597dfef30fcbd90b747474bd749d45f/coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", size = 217025, upload-time = "2025-08-29T15:32:57.169Z" }, - { url = "https://files.pythonhosted.org/packages/23/62/b1e0f513417c02cc10ef735c3ee5186df55f190f70498b3702d516aad06f/coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", size = 217419, upload-time = "2025-08-29T15:32:59.908Z" }, - { url = "https://files.pythonhosted.org/packages/e7/16/b800640b7a43e7c538429e4d7223e0a94fd72453a1a048f70bf766f12e96/coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460", size = 244180, upload-time = "2025-08-29T15:33:01.608Z" }, - { url = "https://files.pythonhosted.org/packages/fb/6f/5e03631c3305cad187eaf76af0b559fff88af9a0b0c180d006fb02413d7a/coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", size = 245992, upload-time = "2025-08-29T15:33:03.239Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a1/f30ea0fb400b080730125b490771ec62b3375789f90af0bb68bfb8a921d7/coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", size = 247851, upload-time = "2025-08-29T15:33:04.603Z" }, - { url = "https://files.pythonhosted.org/packages/02/8e/cfa8fee8e8ef9a6bb76c7bef039f3302f44e615d2194161a21d3d83ac2e9/coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", size = 245891, upload-time = "2025-08-29T15:33:06.176Z" }, - { url = "https://files.pythonhosted.org/packages/93/a9/51be09b75c55c4f6c16d8d73a6a1d46ad764acca0eab48fa2ffaef5958fe/coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945", size = 243909, upload-time = "2025-08-29T15:33:07.74Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a6/ba188b376529ce36483b2d585ca7bdac64aacbe5aa10da5978029a9c94db/coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", size = 244786, upload-time = "2025-08-29T15:33:08.965Z" }, - { url = "https://files.pythonhosted.org/packages/d0/4c/37ed872374a21813e0d3215256180c9a382c3f5ced6f2e5da0102fc2fd3e/coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1", size = 219521, upload-time = "2025-08-29T15:33:10.599Z" }, - { url = "https://files.pythonhosted.org/packages/8e/36/9311352fdc551dec5b973b61f4e453227ce482985a9368305880af4f85dd/coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528", size = 220417, upload-time = "2025-08-29T15:33:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129, upload-time = "2025-08-29T15:33:13.575Z" }, - { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532, upload-time = "2025-08-29T15:33:14.872Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", size = 247931, upload-time = "2025-08-29T15:33:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864, upload-time = "2025-08-29T15:33:17.434Z" }, - { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969, upload-time = "2025-08-29T15:33:18.822Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659, upload-time = "2025-08-29T15:33:20.407Z" }, - { url = "https://files.pythonhosted.org/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", size = 247714, upload-time = "2025-08-29T15:33:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351, upload-time = "2025-08-29T15:33:23.105Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", size = 219562, upload-time = "2025-08-29T15:33:24.717Z" }, - { url = "https://files.pythonhosted.org/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", size = 220453, upload-time = "2025-08-29T15:33:26.482Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", size = 219127, upload-time = "2025-08-29T15:33:27.777Z" }, - { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324, upload-time = "2025-08-29T15:33:29.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560, upload-time = "2025-08-29T15:33:30.748Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/52136173c14e26dfed8b106ed725811bb53c30b896d04d28d74cb64318b3/coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", size = 249053, upload-time = "2025-08-29T15:33:32.041Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802, upload-time = "2025-08-29T15:33:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935, upload-time = "2025-08-29T15:33:34.909Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855, upload-time = "2025-08-29T15:33:36.922Z" }, - { url = "https://files.pythonhosted.org/packages/33/6a/95c32b558d9a61858ff9d79580d3877df3eb5bc9eed0941b1f187c89e143/coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", size = 248974, upload-time = "2025-08-29T15:33:38.175Z" }, - { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409, upload-time = "2025-08-29T15:33:39.447Z" }, - { url = "https://files.pythonhosted.org/packages/04/12/7a55b0bdde78a98e2eb2356771fd2dcddb96579e8342bb52aa5bc52e96f0/coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", size = 219724, upload-time = "2025-08-29T15:33:41.172Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/32b185b8b8e327802c9efce3d3108d2fe2d9d31f153a0f7ecfd59c773705/coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", size = 220536, upload-time = "2025-08-29T15:33:42.524Z" }, - { url = "https://files.pythonhosted.org/packages/08/3a/d5d8dc703e4998038c3099eaf77adddb00536a3cec08c8dcd556a36a3eb4/coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", size = 219171, upload-time = "2025-08-29T15:33:43.974Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "cryptography" -version = "45.0.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980, upload-time = "2025-09-01T11:15:03.146Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105, upload-time = "2025-09-01T11:13:59.684Z" }, - { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799, upload-time = "2025-09-01T11:14:02.517Z" }, - { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504, upload-time = "2025-09-01T11:14:04.522Z" }, - { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542, upload-time = "2025-09-01T11:14:06.309Z" }, - { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244, upload-time = "2025-09-01T11:14:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975, upload-time = "2025-09-01T11:14:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082, upload-time = "2025-09-01T11:14:11.229Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397, upload-time = "2025-09-01T11:14:12.924Z" }, - { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244, upload-time = "2025-09-01T11:14:14.431Z" }, - { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862, upload-time = "2025-09-01T11:14:16.185Z" }, - { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578, upload-time = "2025-09-01T11:14:17.638Z" }, - { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400, upload-time = "2025-09-01T11:14:18.958Z" }, - { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824, upload-time = "2025-09-01T11:14:20.954Z" }, - { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233, upload-time = "2025-09-01T11:14:22.454Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075, upload-time = "2025-09-01T11:14:24.287Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517, upload-time = "2025-09-01T11:14:25.679Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893, upload-time = "2025-09-01T11:14:27.1Z" }, - { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132, upload-time = "2025-09-01T11:14:28.58Z" }, - { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086, upload-time = "2025-09-01T11:14:30.572Z" }, - { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383, upload-time = "2025-09-01T11:14:32.046Z" }, - { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186, upload-time = "2025-09-01T11:14:33.95Z" }, - { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639, upload-time = "2025-09-01T11:14:35.343Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552, upload-time = "2025-09-01T11:14:36.929Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742, upload-time = "2025-09-01T11:14:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/13/3e/e42f1528ca1ea82256b835191eab1be014e0f9f934b60d98b0be8a38ed70/cryptography-45.0.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252", size = 3572442, upload-time = "2025-09-01T11:14:39.836Z" }, - { url = "https://files.pythonhosted.org/packages/59/aa/e947693ab08674a2663ed2534cd8d345cf17bf6a1facf99273e8ec8986dc/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083", size = 4142233, upload-time = "2025-09-01T11:14:41.305Z" }, - { url = "https://files.pythonhosted.org/packages/24/06/09b6f6a2fc43474a32b8fe259038eef1500ee3d3c141599b57ac6c57612c/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130", size = 4376202, upload-time = "2025-09-01T11:14:43.047Z" }, - { url = "https://files.pythonhosted.org/packages/00/f2/c166af87e95ce6ae6d38471a7e039d3a0549c2d55d74e059680162052824/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4", size = 4141900, upload-time = "2025-09-01T11:14:45.089Z" }, - { url = "https://files.pythonhosted.org/packages/16/b9/e96e0b6cb86eae27ea51fa8a3151535a18e66fe7c451fa90f7f89c85f541/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141", size = 4375562, upload-time = "2025-09-01T11:14:47.166Z" }, - { url = "https://files.pythonhosted.org/packages/36/d0/36e8ee39274e9d77baf7d0dafda680cba6e52f3936b846f0d56d64fec915/cryptography-45.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7", size = 3322781, upload-time = "2025-09-01T11:14:48.747Z" }, - { url = "https://files.pythonhosted.org/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde", size = 3584634, upload-time = "2025-09-01T11:14:50.593Z" }, - { url = "https://files.pythonhosted.org/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34", size = 4149533, upload-time = "2025-09-01T11:14:52.091Z" }, - { url = "https://files.pythonhosted.org/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9", size = 4385557, upload-time = "2025-09-01T11:14:53.551Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae", size = 4149023, upload-time = "2025-09-01T11:14:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b", size = 4385722, upload-time = "2025-09-01T11:14:57.319Z" }, - { url = "https://files.pythonhosted.org/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63", size = 3332908, upload-time = "2025-09-01T11:14:58.78Z" }, -] - -[[package]] -name = "cyclopts" -version = "3.23.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/7a/28b63c43d4c17d6587abcfef648841d39543158bcc47b5d40a03b8831f7a/cyclopts-3.23.1.tar.gz", hash = "sha256:ca6a5e9b326caf156d79f3932e2f88b95629e59fd371c0b3a89732b7619edacb", size = 75161, upload-time = "2025-08-30T17:40:34.396Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/67/ac57fbef5414ce84fe0bdeb497918ab2c781ff2cbf23c1bd91334b225669/cyclopts-3.23.1-py3-none-any.whl", hash = "sha256:8e57c6ea47d72b4b565c6a6c8a9fd56ed048ab4316627991230f4ad24ce2bc29", size = 85222, upload-time = "2025-08-30T17:40:33.005Z" }, -] - -[[package]] -name = "dnspython" -version = "2.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, -] - -[[package]] -name = "docstring-parser" -version = "0.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, -] - -[[package]] -name = "docutils" -version = "0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e9/86/5b41c32ecedcfdb4c77b28b6cb14234f252075f8cdb254531727a35547dd/docutils-0.22.tar.gz", hash = "sha256:ba9d57750e92331ebe7c08a1bbf7a7f8143b86c476acd51528b042216a6aad0f", size = 2277984, upload-time = "2025-07-29T15:20:31.06Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/57/8db39bc5f98f042e0153b1de9fb88e1a409a33cda4dd7f723c2ed71e01f6/docutils-0.22-py3-none-any.whl", hash = "sha256:4ed966a0e96a0477d852f7af31bdcb3adc049fbb35ccba358c2ea8a03287615e", size = 630709, upload-time = "2025-07-29T15:20:28.335Z" }, -] - -[[package]] -name = "email-validator" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, -] - -[[package]] -name = "fastmcp" -version = "2.12.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "mcp" }, - { name = "openapi-core" }, - { name = "openapi-pydantic" }, - { name = "pydantic", extra = ["email"] }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/35/8a/c46759bb41a53187191e5b3d963c0bde54783ecc89186a93c4947607b8e4/fastmcp-2.12.2.tar.gz", hash = "sha256:6d13e2f9be57b99763fc22485f9f603daa23bfbca35a8172baa43b283d6fc1ff", size = 5244547, upload-time = "2025-09-03T21:28:09.869Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/0a/7a8d564b1b9909dbfc36eb93d76410a4acfada6b1e13ee451a753bb6dbc2/fastmcp-2.12.2-py3-none-any.whl", hash = "sha256:0b58d68e819c82078d1fd51989d3d81f2be7382d527308b06df55f4d0a4ec94f", size = 312029, upload-time = "2025-09-03T21:28:08.62Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, -] - -[[package]] -name = "httpx-sse" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998, upload-time = "2025-06-24T13:21:05.71Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054, upload-time = "2025-06-24T13:21:04.772Z" }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "isodate" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.25.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, -] - -[[package]] -name = "jsonschema-path" -version = "0.3.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pathable" }, - { name = "pyyaml" }, - { name = "referencing" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159, upload-time = "2025-01-24T14:33:16.547Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810, upload-time = "2025-01-24T14:33:14.652Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, -] - -[[package]] -name = "lazy-object-proxy" -version = "1.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681, upload-time = "2025-08-22T13:50:06.783Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/2b/d5e8915038acbd6c6a9fcb8aaf923dc184222405d3710285a1fec6e262bc/lazy_object_proxy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61d5e3310a4aa5792c2b599a7a78ccf8687292c8eb09cf187cca8f09cf6a7519", size = 26658, upload-time = "2025-08-22T13:42:23.373Z" }, - { url = "https://files.pythonhosted.org/packages/da/8f/91fc00eeea46ee88b9df67f7c5388e60993341d2a406243d620b2fdfde57/lazy_object_proxy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ca33565f698ac1aece152a10f432415d1a2aa9a42dfe23e5ba2bc255ab91f6", size = 68412, upload-time = "2025-08-22T13:42:24.727Z" }, - { url = "https://files.pythonhosted.org/packages/07/d2/b7189a0e095caedfea4d42e6b6949d2685c354263bdf18e19b21ca9b3cd6/lazy_object_proxy-1.12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01c7819a410f7c255b20799b65d36b414379a30c6f1684c7bd7eb6777338c1b", size = 67559, upload-time = "2025-08-22T13:42:25.875Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/b013840cc43971582ff1ceaf784d35d3a579650eb6cc348e5e6ed7e34d28/lazy_object_proxy-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:029d2b355076710505c9545aef5ab3f750d89779310e26ddf2b7b23f6ea03cd8", size = 66651, upload-time = "2025-08-22T13:42:27.427Z" }, - { url = "https://files.pythonhosted.org/packages/7e/6f/b7368d301c15612fcc4cd00412b5d6ba55548bde09bdae71930e1a81f2ab/lazy_object_proxy-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc6e3614eca88b1c8a625fc0a47d0d745e7c3255b21dac0e30b3037c5e3deeb8", size = 66901, upload-time = "2025-08-22T13:42:28.585Z" }, - { url = "https://files.pythonhosted.org/packages/61/1b/c6b1865445576b2fc5fa0fbcfce1c05fee77d8979fd1aa653dd0f179aefc/lazy_object_proxy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:be5fe974e39ceb0d6c9db0663c0464669cf866b2851c73971409b9566e880eab", size = 26536, upload-time = "2025-08-22T13:42:29.636Z" }, - { url = "https://files.pythonhosted.org/packages/01/b3/4684b1e128a87821e485f5a901b179790e6b5bc02f89b7ee19c23be36ef3/lazy_object_proxy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1cf69cd1a6c7fe2dbcc3edaa017cf010f4192e53796538cc7d5e1fedbfa4bcff", size = 26656, upload-time = "2025-08-22T13:42:30.605Z" }, - { url = "https://files.pythonhosted.org/packages/3a/03/1bdc21d9a6df9ff72d70b2ff17d8609321bea4b0d3cffd2cea92fb2ef738/lazy_object_proxy-1.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efff4375a8c52f55a145dc8487a2108c2140f0bec4151ab4e1843e52eb9987ad", size = 68832, upload-time = "2025-08-22T13:42:31.675Z" }, - { url = "https://files.pythonhosted.org/packages/3d/4b/5788e5e8bd01d19af71e50077ab020bc5cce67e935066cd65e1215a09ff9/lazy_object_proxy-1.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1192e8c2f1031a6ff453ee40213afa01ba765b3dc861302cd91dbdb2e2660b00", size = 69148, upload-time = "2025-08-22T13:42:32.876Z" }, - { url = "https://files.pythonhosted.org/packages/79/0e/090bf070f7a0de44c61659cb7f74c2fe02309a77ca8c4b43adfe0b695f66/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3605b632e82a1cbc32a1e5034278a64db555b3496e0795723ee697006b980508", size = 67800, upload-time = "2025-08-22T13:42:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d2/b320325adbb2d119156f7c506a5fbfa37fcab15c26d13cf789a90a6de04e/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a61095f5d9d1a743e1e20ec6d6db6c2ca511961777257ebd9b288951b23b44fa", size = 68085, upload-time = "2025-08-22T13:42:35.197Z" }, - { url = "https://files.pythonhosted.org/packages/6a/48/4b718c937004bf71cd82af3713874656bcb8d0cc78600bf33bb9619adc6c/lazy_object_proxy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:997b1d6e10ecc6fb6fe0f2c959791ae59599f41da61d652f6c903d1ee58b7370", size = 26535, upload-time = "2025-08-22T13:42:36.521Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1b/b5f5bd6bda26f1e15cd3232b223892e4498e34ec70a7f4f11c401ac969f1/lazy_object_proxy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede", size = 26746, upload-time = "2025-08-22T13:42:37.572Z" }, - { url = "https://files.pythonhosted.org/packages/55/64/314889b618075c2bfc19293ffa9153ce880ac6153aacfd0a52fcabf21a66/lazy_object_proxy-1.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9", size = 71457, upload-time = "2025-08-22T13:42:38.743Z" }, - { url = "https://files.pythonhosted.org/packages/11/53/857fc2827fc1e13fbdfc0ba2629a7d2579645a06192d5461809540b78913/lazy_object_proxy-1.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0", size = 71036, upload-time = "2025-08-22T13:42:40.184Z" }, - { url = "https://files.pythonhosted.org/packages/2b/24/e581ffed864cd33c1b445b5763d617448ebb880f48675fc9de0471a95cbc/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308", size = 69329, upload-time = "2025-08-22T13:42:41.311Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/15f8f5a0b0b2e668e756a152257d26370132c97f2f1943329b08f057eff0/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23", size = 70690, upload-time = "2025-08-22T13:42:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/5d/aa/f02be9bbfb270e13ee608c2b28b8771f20a5f64356c6d9317b20043c6129/lazy_object_proxy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073", size = 26563, upload-time = "2025-08-22T13:42:43.685Z" }, - { url = "https://files.pythonhosted.org/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745, upload-time = "2025-08-22T13:42:44.982Z" }, - { url = "https://files.pythonhosted.org/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537, upload-time = "2025-08-22T13:42:46.141Z" }, - { url = "https://files.pythonhosted.org/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141, upload-time = "2025-08-22T13:42:47.375Z" }, - { url = "https://files.pythonhosted.org/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449, upload-time = "2025-08-22T13:42:48.49Z" }, - { url = "https://files.pythonhosted.org/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744, upload-time = "2025-08-22T13:42:49.608Z" }, - { url = "https://files.pythonhosted.org/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568, upload-time = "2025-08-22T13:42:57.719Z" }, - { url = "https://files.pythonhosted.org/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391, upload-time = "2025-08-22T13:42:50.62Z" }, - { url = "https://files.pythonhosted.org/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552, upload-time = "2025-08-22T13:42:51.731Z" }, - { url = "https://files.pythonhosted.org/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857, upload-time = "2025-08-22T13:42:53.225Z" }, - { url = "https://files.pythonhosted.org/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833, upload-time = "2025-08-22T13:42:54.391Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516, upload-time = "2025-08-22T13:42:55.812Z" }, - { url = "https://files.pythonhosted.org/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656, upload-time = "2025-08-22T13:42:56.793Z" }, - { url = "https://files.pythonhosted.org/packages/ef/3a/277857b51ae419a1574557c0b12e0d06bf327b758ba94cafc664cb1e2f66/lazy_object_proxy-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3", size = 26582, upload-time = "2025-08-22T13:49:49.366Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b6/c5e0fa43535bb9c87880e0ba037cdb1c50e01850b0831e80eb4f4762f270/lazy_object_proxy-1.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a", size = 71059, upload-time = "2025-08-22T13:49:50.488Z" }, - { url = "https://files.pythonhosted.org/packages/06/8a/7dcad19c685963c652624702f1a968ff10220b16bfcc442257038216bf55/lazy_object_proxy-1.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a", size = 71034, upload-time = "2025-08-22T13:49:54.224Z" }, - { url = "https://files.pythonhosted.org/packages/12/ac/34cbfb433a10e28c7fd830f91c5a348462ba748413cbb950c7f259e67aa7/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95", size = 69529, upload-time = "2025-08-22T13:49:55.29Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6a/11ad7e349307c3ca4c0175db7a77d60ce42a41c60bcb11800aabd6a8acb8/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5", size = 70391, upload-time = "2025-08-22T13:49:56.35Z" }, - { url = "https://files.pythonhosted.org/packages/59/97/9b410ed8fbc6e79c1ee8b13f8777a80137d4bc189caf2c6202358e66192c/lazy_object_proxy-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f", size = 26988, upload-time = "2025-08-22T13:49:57.302Z" }, - { url = "https://files.pythonhosted.org/packages/41/a0/b91504515c1f9a299fc157967ffbd2f0321bce0516a3d5b89f6f4cad0355/lazy_object_proxy-1.12.0-pp39.pp310.pp311.graalpy311-none-any.whl", hash = "sha256:c3b2e0af1f7f77c4263759c4824316ce458fabe0fceadcd24ef8ca08b2d1e402", size = 15072, upload-time = "2025-08-22T13:50:05.498Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, -] - -[[package]] -name = "mcp" -version = "1.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/3c/82c400c2d50afdac4fbefb5b4031fd327e2ad1f23ccef8eee13c5909aa48/mcp-1.13.1.tar.gz", hash = "sha256:165306a8fd7991dc80334edd2de07798175a56461043b7ae907b279794a834c5", size = 438198, upload-time = "2025-08-22T09:22:16.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/3f/d085c7f49ade6d273b185d61ec9405e672b6433f710ea64a90135a8dd445/mcp-1.13.1-py3-none-any.whl", hash = "sha256:c314e7c8bd477a23ba3ef472ee5a32880316c42d03e06dcfa31a1cc7a73b65df", size = 161494, upload-time = "2025-08-22T09:22:14.705Z" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "more-itertools" -version = "10.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, -] - -[[package]] -name = "mypy" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, - { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, - { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, - { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, - { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, - { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, - { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, - { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, - { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, - { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, - { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, - { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, - { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, - { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, - { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, -] - -[[package]] -name = "openapi-core" -version = "0.19.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "isodate" }, - { name = "jsonschema" }, - { name = "jsonschema-path" }, - { name = "more-itertools" }, - { name = "openapi-schema-validator" }, - { name = "openapi-spec-validator" }, - { name = "parse" }, - { name = "typing-extensions" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/35/1acaa5f2fcc6e54eded34a2ec74b479439c4e469fc4e8d0e803fda0234db/openapi_core-0.19.5.tar.gz", hash = "sha256:421e753da56c391704454e66afe4803a290108590ac8fa6f4a4487f4ec11f2d3", size = 103264, upload-time = "2025-03-20T20:17:28.193Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/6f/83ead0e2e30a90445ee4fc0135f43741aebc30cca5b43f20968b603e30b6/openapi_core-0.19.5-py3-none-any.whl", hash = "sha256:ef7210e83a59394f46ce282639d8d26ad6fc8094aa904c9c16eb1bac8908911f", size = 106595, upload-time = "2025-03-20T20:17:26.77Z" }, -] - -[[package]] -name = "openapi-pydantic" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, -] - -[[package]] -name = "openapi-schema-validator" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema" }, - { name = "jsonschema-specifications" }, - { name = "rfc3339-validator" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/5507ad3325169347cd8ced61c232ff3df70e2b250c49f0fe140edb4973c6/openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee", size = 11550, upload-time = "2025-01-10T18:08:22.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/c6/ad0fba32775ae749016829dace42ed80f4407b171da41313d1a3a5f102e4/openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3", size = 8755, upload-time = "2025-01-10T18:08:19.758Z" }, -] - -[[package]] -name = "openapi-spec-validator" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema" }, - { name = "jsonschema-path" }, - { name = "lazy-object-proxy" }, - { name = "openapi-schema-validator" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/af/fe2d7618d6eae6fb3a82766a44ed87cd8d6d82b4564ed1c7cfb0f6378e91/openapi_spec_validator-0.7.2.tar.gz", hash = "sha256:cc029309b5c5dbc7859df0372d55e9d1ff43e96d678b9ba087f7c56fc586f734", size = 36855, upload-time = "2025-06-07T14:48:56.299Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/dd/b3fd642260cb17532f66cc1e8250f3507d1e580483e209dc1e9d13bd980d/openapi_spec_validator-0.7.2-py3-none-any.whl", hash = "sha256:4bbdc0894ec85f1d1bea1d6d9c8b2c3c8d7ccaa13577ef40da9c006c9fd0eb60", size = 39713, upload-time = "2025-06-07T14:48:54.077Z" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "parse" -version = "1.20.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/78/d9b09ba24bb36ef8b83b71be547e118d46214735b6dfb39e4bfde0e9b9dd/parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce", size = 29391, upload-time = "2024-06-11T04:41:57.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126, upload-time = "2024-06-11T04:41:55.057Z" }, -] - -[[package]] -name = "pathable" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124, upload-time = "2025-01-10T18:43:13.247Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pycparser" -version = "2.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, -] - -[[package]] -name = "pydantic" -version = "2.11.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, - { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, - { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, - { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, - { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, - { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, - { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, - { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, - { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, - { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, - { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, - { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, - { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, - { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, - { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, - { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, - { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, - { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, - { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, -] - -[[package]] -name = "pyperclip" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload-time = "2024-06-18T20:38:48.401Z" } - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, -] - -[[package]] -name = "pytest-cov" -version = "6.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/4c/f883ab8f0daad69f47efdf95f55a66b51a8b939c430dadce0611508d9e99/pytest_cov-6.3.0.tar.gz", hash = "sha256:35c580e7800f87ce892e687461166e1ac2bcb8fb9e13aea79032518d6e503ff2", size = 70398, upload-time = "2025-09-06T15:40:14.361Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/b4/bb7263e12aade3842b938bc5c6958cae79c5ee18992f9b9349019579da0f/pytest_cov-6.3.0-py3-none-any.whl", hash = "sha256:440db28156d2468cafc0415b4f8e50856a0d11faefa38f30906048fe490f1749", size = 25115, upload-time = "2025-09-06T15:40:12.44Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/99/3323ee5c16b3637b4d941c362182d3e749c11e400bea31018c42219f3a98/pytest_mock-3.15.0.tar.gz", hash = "sha256:ab896bd190316b9d5d87b277569dfcdf718b2d049a2ccff5f7aca279c002a1cf", size = 33838, upload-time = "2025-09-04T20:57:48.679Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/b3/7fefc43fb706380144bcd293cc6e446e6f637ddfa8b83f48d1734156b529/pytest_mock-3.15.0-py3-none-any.whl", hash = "sha256:ef2219485fb1bd256b00e7ad7466ce26729b30eadfc7cbcdb4fa9a92ca68db6f", size = 10050, upload-time = "2025-09-04T20:57:47.274Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, - { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, - { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, - { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, - { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, - { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, - { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, - { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, - { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, -] - -[[package]] -name = "qiskit-code-assistant-mcp-server" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "fastmcp" }, - { name = "httpx" }, - { name = "nest-asyncio" }, - { name = "python-dotenv" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "ruff" }, -] -test = [ - { name = "httpx" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "respx" }, -] - -[package.metadata] -requires-dist = [ - { name = "fastmcp", specifier = ">=2.8.1" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "nest-asyncio", specifier = ">=1.5.0" }, - { name = "python-dotenv", specifier = ">=1.0.0" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy", specifier = ">=1.15.0" }, - { name = "ruff", specifier = ">=0.11.13" }, -] -test = [ - { name = "httpx", specifier = ">=0.25.0" }, - { name = "pytest", specifier = ">=7.4.0" }, - { name = "pytest-asyncio", specifier = ">=0.21.0" }, - { name = "pytest-cov", specifier = ">=4.1.0" }, - { name = "pytest-mock", specifier = ">=3.11.0" }, - { name = "respx", specifier = ">=0.20.0" }, -] - -[[package]] -name = "referencing" -version = "0.36.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, -] - -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - -[[package]] -name = "respx" -version = "0.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, -] - -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, -] - -[[package]] -name = "rich" -version = "14.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b0/69/5514c3a87b5f10f09a34bb011bc0927bc12c596c8dae5915604e71abc386/rich_rst-1.3.1.tar.gz", hash = "sha256:fad46e3ba42785ea8c1785e2ceaa56e0ffa32dbe5410dec432f37e4107c4f383", size = 13839, upload-time = "2024-04-30T04:40:38.125Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/bc/cc4e3dbc5e7992398dcb7a8eda0cbcf4fb792a0cdb93f857b478bf3cf884/rich_rst-1.3.1-py3-none-any.whl", hash = "sha256:498a74e3896507ab04492d326e794c3ef76e7cda078703aa592d1853d91098c1", size = 11621, upload-time = "2024-04-30T04:40:32.619Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" }, - { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" }, - { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" }, - { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" }, - { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" }, - { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" }, - { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" }, - { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" }, - { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" }, - { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" }, - { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" }, - { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, - { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, - { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, - { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, - { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, - { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" }, - { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" }, - { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" }, - { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" }, - { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" }, - { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" }, - { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" }, - { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" }, - { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" }, - { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" }, - { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" }, - { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" }, - { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" }, - { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" }, - { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" }, - { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" }, - { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" }, - { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" }, - { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" }, - { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" }, - { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" }, - { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" }, - { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" }, - { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" }, - { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" }, - { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" }, - { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" }, - { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" }, - { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" }, - { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" }, - { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" }, - { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" }, - { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" }, - { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" }, - { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" }, - { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" }, - { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" }, - { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" }, - { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" }, - { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" }, - { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" }, - { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" }, -] - -[[package]] -name = "ruff" -version = "0.12.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" }, - { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" }, - { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" }, - { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" }, - { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" }, - { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" }, - { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" }, - { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" }, - { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" }, - { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985, upload-time = "2025-07-27T09:07:44.565Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297, upload-time = "2025-07-27T09:07:43.268Z" }, -] - -[[package]] -name = "starlette" -version = "0.47.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" }, -] - -[[package]] -name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, -] - -[[package]] -name = "urllib3" -version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, -] - -[[package]] -name = "werkzeug" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/af/d4502dc713b4ccea7175d764718d5183caf8d0867a4f0190d5d4a45cea49/werkzeug-3.1.1.tar.gz", hash = "sha256:8cd39dfbdfc1e051965f156163e2974e52c210f130810e9ad36858f0fd3edad4", size = 806453, upload-time = "2024-11-01T16:40:45.462Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/ea/c67e1dee1ba208ed22c06d1d547ae5e293374bfc43e0eb0ef5e262b68561/werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5", size = 224371, upload-time = "2024-11-01T16:40:43.994Z" }, -] diff --git a/qiskit-docs-mcp-server/src/qiskit_docs_mcp_server/constants.py b/qiskit-docs-mcp-server/src/qiskit_docs_mcp_server/constants.py index b06ec26..635f411 100644 --- a/qiskit-docs-mcp-server/src/qiskit_docs_mcp_server/constants.py +++ b/qiskit-docs-mcp-server/src/qiskit_docs_mcp_server/constants.py @@ -242,11 +242,6 @@ def _get_env_float(name: str, default: float) -> float: "qiskit-addons-sqd", "qiskit-addons-sqd-get-started", "qiskit-backendv1-to-v2", - "qiskit-code-assistant", - "qiskit-code-assistant-jupyterlab", - "qiskit-code-assistant-local", - "qiskit-code-assistant-openai-api", - "qiskit-code-assistant-vscode", "qiskit-function-templates", "qiskit-mcp-servers", "qiskit-runtime-circuit-timing", diff --git a/ruff.toml b/ruff.toml index ef1bc07..948b8a3 100644 --- a/ruff.toml +++ b/ruff.toml @@ -165,7 +165,7 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" ] [lint.isort] -known-first-party = ["qiskit_code_assistant_mcp_server", "qiskit_ibm_runtime_mcp_server", "qiskit_gym_mcp_server"] +known-first-party = ["qiskit_ibm_runtime_mcp_server", "qiskit_gym_mcp_server"] force-single-line = false lines-after-imports = 2 section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] diff --git a/src/qiskit_mcp_servers/__init__.py b/src/qiskit_mcp_servers/__init__.py index 63c4191..a28283d 100644 --- a/src/qiskit_mcp_servers/__init__.py +++ b/src/qiskit_mcp_servers/__init__.py @@ -2,13 +2,15 @@ Qiskit MCP Servers - Meta-package for all Qiskit MCP servers. This is a meta-package that installs all available Qiskit MCP servers: -- qiskit-code-assistant-mcp-server: MCP server for Qiskit Code Assistant +- qiskit-mcp-server: MCP server for Qiskit (circuits, transpilation, serialization) +- qiskit-docs-mcp-server: MCP server for Qiskit documentation retrieval - qiskit-ibm-runtime-mcp-server: MCP server for IBM Quantum Runtime +- qiskit-ibm-transpiler-mcp-server: MCP server for the AI-powered transpiler Usage: pip install qiskit-mcp-servers To install individual servers: - pip install qiskit-mcp-servers[code-assistant] + pip install qiskit-mcp-servers[qiskit] pip install qiskit-mcp-servers[runtime] """ diff --git a/uv.lock b/uv.lock index 00a55f5..9dc3fa9 100644 --- a/uv.lock +++ b/uv.lock @@ -10,7 +10,6 @@ resolution-markers = [ [manifest] members = [ - "qiskit-code-assistant-mcp-server", "qiskit-docs-mcp-server", "qiskit-gym-mcp-server", "qiskit-ibm-runtime-mcp-server", @@ -4538,107 +4537,6 @@ qpy-compat = [ { name = "sympy" }, ] -[[package]] -name = "qiskit-code-assistant-mcp-server" -version = "0.4.0" -source = { editable = "qiskit-code-assistant-mcp-server" } -dependencies = [ - { name = "fastmcp" }, - { name = "httpx" }, - { name = "nest-asyncio" }, - { name = "python-dotenv" }, -] - -[package.optional-dependencies] -all = [ - { name = "bandit", extra = ["toml"] }, - { name = "httpx" }, - { name = "mypy" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "respx" }, - { name = "ruff" }, -] -dev = [ - { name = "bandit", extra = ["toml"] }, - { name = "mypy" }, - { name = "pre-commit" }, - { name = "ruff" }, -] -test = [ - { name = "httpx" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "respx" }, -] - -[package.dev-dependencies] -dev = [ - { name = "bandit", extra = ["toml"] }, - { name = "mypy" }, - { name = "ruff" }, -] -examples = [ - { name = "langchain" }, - { name = "langchain-anthropic" }, - { name = "langchain-mcp-adapters" }, - { name = "python-dotenv" }, -] -test = [ - { name = "httpx" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "respx" }, -] - -[package.metadata] -requires-dist = [ - { name = "bandit", extras = ["toml"], marker = "extra == 'dev'", specifier = ">=1.7.0" }, - { name = "fastmcp", specifier = ">=2.8.1,<3" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "httpx", marker = "extra == 'test'", specifier = ">=0.25.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.15.0" }, - { name = "nest-asyncio", specifier = ">=1.5.0" }, - { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.5.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=7.4.0" }, - { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.21.0" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=4.1.0" }, - { name = "pytest-mock", marker = "extra == 'test'", specifier = ">=3.11.0" }, - { name = "python-dotenv", specifier = ">=1.0.0" }, - { name = "qiskit-code-assistant-mcp-server", extras = ["dev", "test"], marker = "extra == 'all'", editable = "qiskit-code-assistant-mcp-server" }, - { name = "respx", marker = "extra == 'test'", specifier = ">=0.20.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11.13" }, -] -provides-extras = ["dev", "test", "all"] - -[package.metadata.requires-dev] -dev = [ - { name = "bandit", extras = ["toml"], specifier = ">=1.9.2" }, - { name = "mypy", specifier = ">=1.15.0" }, - { name = "ruff", specifier = ">=0.11.13" }, -] -examples = [ - { name = "langchain", specifier = ">=1.2.0" }, - { name = "langchain-anthropic", specifier = ">=1.0.0" }, - { name = "langchain-mcp-adapters", specifier = ">=0.1.0" }, - { name = "python-dotenv", specifier = ">=1.0.0" }, -] -test = [ - { name = "httpx", specifier = ">=0.25.0" }, - { name = "pytest", specifier = ">=7.4.0" }, - { name = "pytest-asyncio", specifier = ">=0.21.0" }, - { name = "pytest-cov", specifier = ">=4.1.0" }, - { name = "pytest-mock", specifier = ">=3.11.0" }, - { name = "respx", specifier = ">=0.20.0" }, -] - [[package]] name = "qiskit-docs-mcp-server" version = "0.2.0" @@ -5205,10 +5103,9 @@ test = [ [[package]] name = "qiskit-mcp-servers" -version = "0.10.0" +version = "0.11.0" source = { editable = "." } dependencies = [ - { name = "qiskit-code-assistant-mcp-server" }, { name = "qiskit-docs-mcp-server" }, { name = "qiskit-ibm-runtime-mcp-server" }, { name = "qiskit-ibm-transpiler-mcp-server" }, @@ -5217,16 +5114,12 @@ dependencies = [ [package.optional-dependencies] all = [ - { name = "qiskit-code-assistant-mcp-server" }, { name = "qiskit-docs-mcp-server" }, { name = "qiskit-gym-mcp-server" }, { name = "qiskit-ibm-runtime-mcp-server" }, { name = "qiskit-ibm-transpiler-mcp-server" }, { name = "qiskit-mcp-server" }, ] -code-assistant = [ - { name = "qiskit-code-assistant-mcp-server" }, -] community = [ { name = "qiskit-gym-mcp-server" }, ] @@ -5261,9 +5154,6 @@ examples = [ [package.metadata] requires-dist = [ - { name = "qiskit-code-assistant-mcp-server", editable = "qiskit-code-assistant-mcp-server" }, - { name = "qiskit-code-assistant-mcp-server", marker = "extra == 'all'", editable = "qiskit-code-assistant-mcp-server" }, - { name = "qiskit-code-assistant-mcp-server", marker = "extra == 'code-assistant'", editable = "qiskit-code-assistant-mcp-server" }, { name = "qiskit-docs-mcp-server", editable = "qiskit-docs-mcp-server" }, { name = "qiskit-docs-mcp-server", marker = "extra == 'all'", editable = "qiskit-docs-mcp-server" }, { name = "qiskit-docs-mcp-server", marker = "extra == 'docs'", editable = "qiskit-docs-mcp-server" }, @@ -5280,7 +5170,7 @@ requires-dist = [ { name = "qiskit-mcp-server", marker = "extra == 'all'", editable = "qiskit-mcp-server" }, { name = "qiskit-mcp-server", marker = "extra == 'qiskit'", editable = "qiskit-mcp-server" }, ] -provides-extras = ["qiskit", "code-assistant", "docs", "runtime", "transpiler", "gym", "all", "community"] +provides-extras = ["qiskit", "docs", "runtime", "transpiler", "gym", "all", "community"] [package.metadata.requires-dev] dev = [ @@ -5544,18 +5434,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, ] -[[package]] -name = "respx" -version = "0.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, -] - [[package]] name = "rfc3339-validator" version = "0.1.4"